Files
react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManagerDelegate.java
T
fabriziobertoglio1987 dd6325bafe TalkBack support for ScrollView accessibility announcements (list and grid) (#33180)
Summary:
This issue fixes [30977][17] . The Pull Request was previously published by [intergalacticspacehighway][13] with [31666][19].
The solution consists of:
1. Adding Javascript logic in the [FlatList][14], SectionList, VirtualizedList components to provide accessibility information (row and column position) for each cell in the method [renderItem][20] as a fourth parameter [accessibilityCollectionItem][21]. The information is saved on the native side in the AccessibilityNodeInfo and announced by TalkBack when changing row, column, or page ([video example][12]). The prop accessibilityCollectionItem is available in the View component which wraps each FlatList cell.
2. Adding Java logic in [ReactScrollView.java][16] and HorizontalScrollView to announce pages with TalkBack when scrolling up/down. The missing AOSP logic in [ScrollView.java][10] (see also the [GridView][11] example) is responsible for announcing Page Scrolling with TalkBack.

Relevant Links:
x [Additional notes on this PR][18]
x [discussion on the additional container View around each FlatList cell][22]
x [commit adding prop getCellsInItemCount to VirtualizedList][23]

## Changelog

[Android] [Added] - Accessibility announcement for list and grid in FlatList

Pull Request resolved: https://github.com/facebook/react-native/pull/33180

Test Plan:
[1]. TalkBack announces pages and cells with Horizontal Flatlist in the Paper Renderer ([link][1])
[2]. TalkBack announces pages and cells with Vertical Flatlist in the Paper Renderer ([link][2])
[3]. `FlatList numColumns={undefined}` Should not trigger Runtime Error NoSuchKey exception columnCount when enabling TalkBack. ([link][3])
[4]. TalkBack announces pages and cells with Nested Horizontal Flatlist in the rn-tester app ([link][4])

[1]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1050452894
[2]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1050462465
[3]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1032340879
[4]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1050618308
[10]:https://github.com/aosp-mirror/platform_frameworks_base/blob/1ac46f932ef88a8f96d652580d8105e361ffc842/core/java/android/widget/AdapterView.java#L1027-L1029 "GridView.java method responsible for calling setFromIndex and setToIndex"
[11]:https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1042518901 "test case on Android GridView"
[12]:https://github.com/fabriziobertoglio1987/react-native-notes/issues/6#issuecomment-1050452894 "TalkBack announces pages and cells with Horizontal Flatlist in the Paper Renderer"
[13]:https://github.com/intergalacticspacehighway "github intergalacticspacehighway"
[14]:https://github.com/fabriziobertoglio1987/react-native/blob/80acf523a4410adac8005d5c9472fb87f78e12ee/Libraries/Lists/FlatList.js#L617-L636 "FlatList accessibilityCollectionItem"
[16]:https://github.com/fabriziobertoglio1987/react-native/blob/5706bd7d3ee35dca48f85322a2bdcaec0bce2c85/ReactAndroid/src/main/java/com/facebook/react/views/scroll/ReactScrollView.java#L183-L184 "logic added to ReactScrollView.java"
[17]: https://github.com/facebook/react-native/issues/30977
[18]: https://github.com/fabriziobertoglio1987/react-native-notes/issues/6
[19]: https://github.com/facebook/react-native/pull/31666
[20]: https://reactnative.dev/docs/next/flatlist#required-renderitem "FlatList renderItem documentation"
[21]: https://github.com/fabriziobertoglio1987/react-native/commit/75147359c5d070406ebbe488c57c3cd94c08c19d "commit that introduces fourth param accessibilityCollectionItem in callback renderItem"
[22]: https://github.com/facebook/react-native/pull/33180#discussion_r826748664 "discussion on the additional container View around each FlatList cell"
[23]: https://github.com/fabriziobertoglio1987/react-native/commit/d50fd1a68112f40f4be3ac3aa4d67f96df33e387 "commit adding prop getCellsInItemCount to VirtualizedList"

Reviewed By: kacieb

Differential Revision: D34518929

Pulled By: blavalla

fbshipit-source-id: 410a05263a56162bf505a4cad957b24005ed65ed
2022-04-19 19:45:10 -07:00

134 lines
5.4 KiB
Java

/*
* 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.
*/
package com.facebook.react.uimanager;
import android.view.View;
import androidx.annotation.Nullable;
import com.facebook.react.bridge.ColorPropConverter;
import com.facebook.react.bridge.Dynamic;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.yoga.YogaConstants;
/**
* This is a base implementation of {@link ViewManagerDelegate} which supports setting properties
* that every view should support, such as rotation, background color, etc.
*/
public abstract class BaseViewManagerDelegate<T extends View, U extends BaseViewManagerInterface<T>>
implements ViewManagerDelegate<T> {
protected final U mViewManager;
public BaseViewManagerDelegate(U viewManager) {
mViewManager = viewManager;
}
@Override
public void setProperty(T view, String propName, @Nullable Object value) {
switch (propName) {
case ViewProps.ACCESSIBILITY_ACTIONS:
mViewManager.setAccessibilityActions(view, (ReadableArray) value);
break;
case ViewProps.ACCESSIBILITY_HINT:
mViewManager.setAccessibilityHint(view, (String) value);
break;
case ViewProps.ACCESSIBILITY_LABEL:
mViewManager.setAccessibilityLabel(view, (String) value);
break;
case ViewProps.ACCESSIBILITY_LIVE_REGION:
mViewManager.setAccessibilityLiveRegion(view, (String) value);
break;
case ViewProps.ACCESSIBILITY_ROLE:
mViewManager.setAccessibilityRole(view, (String) value);
break;
case ViewProps.ACCESSIBILITY_STATE:
mViewManager.setViewState(view, (ReadableMap) value);
break;
case ViewProps.ACCESSIBILITY_COLLECTION:
mViewManager.setAccessibilityCollection(view, (ReadableMap) value);
break;
case ViewProps.ACCESSIBILITY_COLLECTION_ITEM:
mViewManager.setAccessibilityCollectionItem(view, (ReadableMap) value);
break;
case ViewProps.BACKGROUND_COLOR:
mViewManager.setBackgroundColor(
view, value == null ? 0 : ColorPropConverter.getColor(value, view.getContext()));
break;
case ViewProps.BORDER_RADIUS:
mViewManager.setBorderRadius(
view, value == null ? YogaConstants.UNDEFINED : ((Double) value).floatValue());
break;
case ViewProps.BORDER_BOTTOM_LEFT_RADIUS:
mViewManager.setBorderBottomLeftRadius(
view, value == null ? YogaConstants.UNDEFINED : ((Double) value).floatValue());
break;
case ViewProps.BORDER_BOTTOM_RIGHT_RADIUS:
mViewManager.setBorderBottomRightRadius(
view, value == null ? YogaConstants.UNDEFINED : ((Double) value).floatValue());
break;
case ViewProps.BORDER_TOP_LEFT_RADIUS:
mViewManager.setBorderTopLeftRadius(
view, value == null ? YogaConstants.UNDEFINED : ((Double) value).floatValue());
break;
case ViewProps.BORDER_TOP_RIGHT_RADIUS:
mViewManager.setBorderTopRightRadius(
view, value == null ? YogaConstants.UNDEFINED : ((Double) value).floatValue());
break;
case ViewProps.ELEVATION:
mViewManager.setElevation(view, value == null ? 0.0f : ((Double) value).floatValue());
break;
case ViewProps.SHADOW_COLOR:
mViewManager.setShadowColor(
view, value == null ? 0 : ColorPropConverter.getColor(value, view.getContext()));
break;
case ViewProps.IMPORTANT_FOR_ACCESSIBILITY:
mViewManager.setImportantForAccessibility(view, (String) value);
break;
case ViewProps.NATIVE_ID:
mViewManager.setNativeId(view, (String) value);
break;
case ViewProps.ACCESSIBILITY_LABELLED_BY:
mViewManager.setAccessibilityLabelledBy(view, (Dynamic) value);
break;
case ViewProps.OPACITY:
mViewManager.setOpacity(view, value == null ? 1.0f : ((Double) value).floatValue());
break;
case ViewProps.RENDER_TO_HARDWARE_TEXTURE:
//noinspection SimplifiableConditionalExpression
mViewManager.setRenderToHardwareTexture(view, value == null ? false : (boolean) value);
break;
case ViewProps.ROTATION:
mViewManager.setRotation(view, value == null ? 0.0f : ((Double) value).floatValue());
break;
case ViewProps.SCALE_X:
mViewManager.setScaleX(view, value == null ? 1.0f : ((Double) value).floatValue());
break;
case ViewProps.SCALE_Y:
mViewManager.setScaleY(view, value == null ? 1.0f : ((Double) value).floatValue());
break;
case ViewProps.TEST_ID:
mViewManager.setTestId(view, (String) value);
break;
case ViewProps.TRANSFORM:
mViewManager.setTransform(view, (ReadableArray) value);
break;
case ViewProps.TRANSLATE_X:
mViewManager.setTranslateX(view, value == null ? 0.0f : ((Double) value).floatValue());
break;
case ViewProps.TRANSLATE_Y:
mViewManager.setTranslateY(view, value == null ? 0.0f : ((Double) value).floatValue());
break;
case ViewProps.Z_INDEX:
mViewManager.setZIndex(view, value == null ? 0.0f : ((Double) value).floatValue());
break;
}
}
@Override
public void receiveCommand(T view, String commandName, ReadableArray args) {}
}