From 55c0df43b9859853e41b6e2ef271b78b783538f0 Mon Sep 17 00:00:00 2001 From: fabriziobertoglio1987 Date: Mon, 7 Nov 2022 23:25:33 -0800 Subject: [PATCH] Adding pager, scrollview, viewgroup, webview, drawer roles (#34477) Summary: - adds missing roles - adds custom roles that don't exist in TalkBack (see the [compositor.json][10] and [string.xml][11] files). - fixes [issues with Drawer][12] - fixes issues with ScrollView missing roles - seek control already exist as adjustable https://github.com/facebook/react-native/pull/34477/commits/d460d097ac0f5df8002e072711023517905f9ca9 Relevant https://github.com/facebook/react-native/issues/30839#issuecomment-1222293556 fixes https://github.com/facebook/react-native/issues/30839 ## Changelog [Android] [Fixed] - Adding pager, scrollview, viewgroup, webview, drawer roles Pull Request resolved: https://github.com/facebook/react-native/pull/34477 Test Plan: Android - Drawer Layout and ScrollView (02/09/22) https://github.com/facebook/react-native/pull/34477#issuecomment-1235293165 - sliding drawer, drawer layout, icon menu https://github.com/facebook/react-native/pull/34477#issuecomment-1224112650 - Horizontal and Vertical ScrollView https://github.com/facebook/react-native/pull/34477#issuecomment-1225478289 - Toast https://github.com/facebook/react-native/pull/34477#issuecomment-1225369629 - CheckedTextView https://github.com/facebook/react-native/pull/34477#discussion_r959329833 - Spinner (dropdownlist) https://github.com/facebook/react-native/pull/34477#discussion_r959374894 - EditText https://github.com/facebook/react-native/pull/34477#discussion_r959412185 - WebView https://github.com/facebook/react-native/pull/34477#discussion_r959417518 - Testing chime_up and chime_down sound feedback in Scrollable https://github.com/facebook/react-native/pull/34477#issuecomment-1238882030 iOS https://github.com/facebook/react-native/pull/34477#issuecomment-1232418595 [10]: https://github.com/google/talkback/blob/771de7cdbf55b6adb4ca4c64c27a52584f2337cc/compositor/src/main/res/raw/compositor.json#L1082-L1108 [11]: https://github.com/google/talkback/blob/771de7cdbf55b6adb4ca4c64c27a52584f2337cc/compositor/src/main/res/values/strings.xml#L223 [12]: https://github.com/facebook/react-native/pull/34477#issuecomment-1224112650 Reviewed By: NickGerleman Differential Revision: D39894307 Pulled By: blavalla fbshipit-source-id: 4a8da78bae485ead0523689631d88d1031a07b74 --- .../DrawerLayoutAndroid.android.js | 3 + .../Components/View/ViewAccessibility.js | 11 +- React/Views/RCTViewManager.m | 10 + .../uimanager/ReactAccessibilityDelegate.java | 29 +- .../java/com/facebook/react/views/drawer/BUCK | 1 + .../react/views/drawer/ReactDrawerLayout.java | 30 ++ .../Accessibility/AccessibilityExample.js | 274 +++++++++++------- .../DrawerLayoutAndroidExample.js | 100 +++++++ .../js/utils/RNTesterList.android.js | 5 + 9 files changed, 357 insertions(+), 106 deletions(-) create mode 100644 packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js diff --git a/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js b/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js index 10e265c553c..4435044e57a 100644 --- a/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js +++ b/Libraries/Components/DrawerAndroid/DrawerLayoutAndroid.android.js @@ -21,6 +21,7 @@ import dismissKeyboard from '../../Utilities/dismissKeyboard'; import Platform from '../../Utilities/Platform'; import StatusBar from '../StatusBar/StatusBar'; import View from '../View/View'; +import type {AccessibilityRole} from '../../Components/View/ViewAccessibility'; import AndroidDrawerLayoutNativeComponent, { Commands, } from './AndroidDrawerLayoutNativeComponent'; @@ -36,6 +37,8 @@ type DrawerSlideEvent = $ReadOnly<{| |}>; type Props = $ReadOnly<{| + accessibilityRole?: ?AccessibilityRole, + /** * Determines whether the keyboard gets dismissed in response to a drag. * - 'none' (the default), drags do not dismiss the keyboard. diff --git a/Libraries/Components/View/ViewAccessibility.js b/Libraries/Components/View/ViewAccessibility.js index c62be022b04..18da75effcb 100644 --- a/Libraries/Components/View/ViewAccessibility.js +++ b/Libraries/Components/View/ViewAccessibility.js @@ -16,6 +16,7 @@ import type {SyntheticEvent} from '../../Types/CoreEventTypes'; export type AccessibilityRole = | 'none' | 'button' + | 'dropdownlist' | 'togglebutton' | 'link' | 'search' @@ -44,7 +45,15 @@ export type AccessibilityRole = | 'timer' | 'list' | 'toolbar' - | 'grid'; + | 'grid' + | 'pager' + | 'scrollview' + | 'horizontalscrollview' + | 'viewgroup' + | 'webview' + | 'drawerlayout' + | 'slidingdrawer' + | 'iconmenu'; // Role types for web export type Role = diff --git a/React/Views/RCTViewManager.m b/React/Views/RCTViewManager.m index b034ff08ac2..309f9a2fb8f 100644 --- a/React/Views/RCTViewManager.m +++ b/React/Views/RCTViewManager.m @@ -28,6 +28,7 @@ RCT_MULTI_ENUM_CONVERTER( (@{ @"none" : @(UIAccessibilityTraitNone), @"button" : @(UIAccessibilityTraitButton), + @"dropdownlist" : @(UIAccessibilityTraitNone), @"togglebutton" : @(UIAccessibilityTraitButton), @"link" : @(UIAccessibilityTraitLink), @"header" : @(UIAccessibilityTraitHeader), @@ -63,6 +64,15 @@ RCT_MULTI_ENUM_CONVERTER( @"tablist" : @(UIAccessibilityTraitNone), @"timer" : @(UIAccessibilityTraitNone), @"toolbar" : @(UIAccessibilityTraitNone), + @"grid" : @(UIAccessibilityTraitNone), + @"pager" : @(UIAccessibilityTraitNone), + @"scrollview" : @(UIAccessibilityTraitNone), + @"horizontalscrollview" : @(UIAccessibilityTraitNone), + @"viewgroup" : @(UIAccessibilityTraitNone), + @"webview" : @(UIAccessibilityTraitNone), + @"drawerlayout" : @(UIAccessibilityTraitNone), + @"slidingdrawer" : @(UIAccessibilityTraitNone), + @"iconmenu" : @(UIAccessibilityTraitNone), @"list" : @(UIAccessibilityTraitNone), @"grid" : @(UIAccessibilityTraitNone), }), diff --git a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java index 0f5f1671c8f..1ec4a23f73f 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java +++ b/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java @@ -96,6 +96,7 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper { public enum AccessibilityRole { NONE, BUTTON, + DROPDOWNLIST, TOGGLEBUTTON, LINK, SEARCH, @@ -123,12 +124,22 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper { TIMER, LIST, GRID, + PAGER, + SCROLLVIEW, + HORIZONTALSCROLLVIEW, + VIEWGROUP, + WEBVIEW, + DRAWERLAYOUT, + SLIDINGDRAWER, + ICONMENU, TOOLBAR; public static String getValue(AccessibilityRole role) { switch (role) { case BUTTON: return "android.widget.Button"; + case DROPDOWNLIST: + return "android.widget.Spinner"; case TOGGLEBUTTON: return "android.widget.ToggleButton"; case SEARCH: @@ -136,7 +147,7 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper { case IMAGE: return "android.widget.ImageView"; case IMAGEBUTTON: - return "android.widget.ImageButon"; + return "android.widget.ImageButton"; case KEYBOARDKEY: return "android.inputmethodservice.Keyboard$Key"; case TEXT: @@ -155,6 +166,22 @@ public class ReactAccessibilityDelegate extends ExploreByTouchHelper { return "android.widget.AbsListView"; case GRID: return "android.widget.GridView"; + case SCROLLVIEW: + return "android.widget.ScrollView"; + case HORIZONTALSCROLLVIEW: + return "android.widget.HorizontalScrollView"; + case PAGER: + return "androidx.viewpager.widget.ViewPager"; + case DRAWERLAYOUT: + return "androidx.drawerlayout.widget.DrawerLayout"; + case SLIDINGDRAWER: + return "android.widget.SlidingDrawer"; + case ICONMENU: + return "com.android.internal.view.menu.IconMenuView"; + case VIEWGROUP: + return "android.view.ViewGroup"; + case WEBVIEW: + return "android.webkit.WebView"; case NONE: case LINK: case SUMMARY: diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK index 9677362369c..0b70da4c4df 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/BUCK @@ -29,5 +29,6 @@ rn_android_library( react_native_target("java/com/facebook/react/uimanager/annotations:annotations"), react_native_target("java/com/facebook/react/views/scroll:scroll"), react_native_root_target(":generated_components_java-FBReactNativeComponentSpec"), + react_native_target("res:uimanager"), ], ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java index ab9bb7d30d3..40392887a1c 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java +++ b/ReactAndroid/src/main/java/com/facebook/react/views/drawer/ReactDrawerLayout.java @@ -10,10 +10,16 @@ package com.facebook.react.views.drawer; import android.view.Gravity; import android.view.MotionEvent; import android.view.View; +import android.view.accessibility.AccessibilityEvent; +import androidx.core.view.AccessibilityDelegateCompat; +import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.drawerlayout.widget.DrawerLayout; import com.facebook.common.logging.FLog; +import com.facebook.react.R; import com.facebook.react.bridge.ReactContext; import com.facebook.react.common.ReactConstants; +import com.facebook.react.uimanager.ReactAccessibilityDelegate.AccessibilityRole; import com.facebook.react.uimanager.events.NativeGestureUtil; /** @@ -29,6 +35,30 @@ import com.facebook.react.uimanager.events.NativeGestureUtil; public ReactDrawerLayout(ReactContext reactContext) { super(reactContext); + ViewCompat.setAccessibilityDelegate( + this, + new AccessibilityDelegateCompat() { + @Override + public void onInitializeAccessibilityNodeInfo( + View host, AccessibilityNodeInfoCompat info) { + super.onInitializeAccessibilityNodeInfo(host, info); + final AccessibilityRole accessibilityRole = + (AccessibilityRole) host.getTag(R.id.accessibility_role); + if (accessibilityRole != null) { + info.setClassName(AccessibilityRole.getValue(accessibilityRole)); + } + } + + @Override + public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) { + super.onInitializeAccessibilityEvent(host, event); + final AccessibilityRole accessibilityRole = + (AccessibilityRole) host.getTag(R.id.accessibility_role); + if (accessibilityRole != null) { + event.setClassName(AccessibilityRole.getValue(accessibilityRole)); + } + } + }); } @Override diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js index c552f756996..d5caca95b71 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js @@ -28,6 +28,7 @@ const { Slider, Platform, Switch, + ScrollView, } = require('react-native'); import type {EventSubscription} from 'react-native/Libraries/vendor/emitter/EventEmitter'; @@ -84,6 +85,9 @@ const styles = StyleSheet.create({ textAlign: 'center', backgroundColor: '#000000c0', }, + scrollView: { + height: 50, + }, }); class AccessibilityExample extends React.Component<{}> { @@ -571,106 +575,168 @@ class NestedCheckBox extends React.Component< class AccessibilityRoleAndStateExample extends React.Component<{}> { render(): React.Node { + const content = [ + This is some text, + This is some text, + This is some text, + This is some text, + This is some text, + This is some text, + This is some text, + ]; + return ( - - - Alert example - - - - Combobox example - - - Menu example - - - Menu bar example - - - Menu item example - - - Progress bar example - - - Radio button example - - - Radio group example - - - Scrollbar example - - - Spin button example - - - - Tab example - - - Tab list example - - - Timer example - - - Toolbar example - - - State busy example - - - - - + <> + + + {content} + - + + + {content} + + + + + {content} + + + + + + Alert example + + + + Combobox example + + + Menu example + + + Menu bar example + + + Menu item example + + + Progress bar example + + + Radio button example + + + Radio group example + + + Scrollbar example + + + Spin button example + + + + Tab example + + + Tab list example + + + Timer example + + + Toolbar example + + + State busy example + + + Drop Down List example + + + Pager example + + + Toggle Button example + + + Viewgroup example + + + Webview example + + + + + + + + + ); } } @@ -1369,18 +1435,18 @@ exports.title = 'Accessibility'; exports.documentationURL = 'https://reactnative.dev/docs/accessibilityinfo'; exports.description = 'Examples of using Accessibility APIs.'; exports.examples = [ - { - title: 'Accessibility elements', - render(): React.Element { - return ; - }, - }, { title: 'New accessibility roles and states', render(): React.Element { return ; }, }, + { + title: 'Accessibility elements', + render(): React.Element { + return ; + }, + }, { title: 'Accessibility action examples', render(): React.Element { diff --git a/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js b/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js new file mode 100644 index 00000000000..2906099c49d --- /dev/null +++ b/packages/rn-tester/js/examples/DrawerLayoutAndroid/DrawerLayoutAndroidExample.js @@ -0,0 +1,100 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + * + * @format + * @flow + */ + +'use strict'; + +import React, {useRef, useState} from 'react'; +import type {Node} from 'react'; +import { + Button, + DrawerLayoutAndroid, + Text, + StyleSheet, + View, +} from 'react-native'; + +const Drawer = () => { + const drawer = useRef(null); + const [drawerPosition, setDrawerPosition] = useState('left'); + const changeDrawerPosition = () => { + if (drawerPosition === 'left') { + setDrawerPosition('right'); + } else { + setDrawerPosition('left'); + } + }; + + const navigationView = () => ( + + I'm in the Drawer! +