From da72b5d02c23d6a354dece206281eac4c0322490 Mon Sep 17 00:00:00 2001 From: Andrei Shikov Date: Tue, 22 Feb 2022 08:03:09 -0800 Subject: [PATCH] Add accessibility and view props required for Android to C++ layer Summary: Parses a set of props previously missing from C++ representation (they weren't required for iOS and core processing before). Changelog: [Internal] - Added missing fields for Android to C++ view props Reviewed By: sammy-SC Differential Revision: D33797489 fbshipit-source-id: 1625baa0c1a592fcef409a5f206496dff0368912 --- .../components/view/AccessibilityPrimitives.h | 23 ++++++++ .../components/view/AccessibilityProps.cpp | 18 +++++++ .../components/view/AccessibilityProps.h | 4 ++ .../renderer/components/view/ViewProps.cpp | 44 ++++++++++++++- .../renderer/components/view/ViewProps.h | 12 +++++ .../components/view/ViewShadowNode.cpp | 13 ++++- .../view/accessibilityPropsConversions.h | 33 ++++++++++++ .../renderer/components/view/primitives.h | 43 +++++++++++++++ .../components/view/propsConversions.h | 53 +++++++++++++++++++ 9 files changed, 240 insertions(+), 3 deletions(-) diff --git a/ReactCommon/react/renderer/components/view/AccessibilityPrimitives.h b/ReactCommon/react/renderer/components/view/AccessibilityPrimitives.h index 9a6a6cb897..fe851afa1b 100644 --- a/ReactCommon/react/renderer/components/view/AccessibilityPrimitives.h +++ b/ReactCommon/react/renderer/components/view/AccessibilityPrimitives.h @@ -10,6 +10,7 @@ #include #include #include +#include namespace facebook { namespace react { @@ -75,6 +76,22 @@ constexpr bool operator!=( return !(rhs == lhs); } +struct AccessibilityLabelledBy { + std::vector value{}; +}; + +inline static bool operator==( + AccessibilityLabelledBy const &lhs, + AccessibilityLabelledBy const &rhs) { + return lhs.value == rhs.value; +} + +inline static bool operator!=( + AccessibilityLabelledBy const &lhs, + AccessibilityLabelledBy const &rhs) { + return !(lhs == rhs); +} + struct AccessibilityValue { butter::optional min; butter::optional max; @@ -102,5 +119,11 @@ enum class ImportantForAccessibility { NoHideDescendants, }; +enum class AccessibilityLiveRegion { + None, + Polite, + Assertive, +}; + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp b/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp index a911458aa3..98311ae553 100644 --- a/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp +++ b/ReactCommon/react/renderer/components/view/AccessibilityProps.cpp @@ -43,6 +43,24 @@ AccessibilityProps::AccessibilityProps( "accessibilityLabel", sourceProps.accessibilityLabel, "")), + accessibilityLabelledBy(convertRawProp( + context, + rawProps, + "accessibilityLabelledBy", + sourceProps.accessibilityLabelledBy, + {})), + accessibilityLiveRegion(convertRawProp( + context, + rawProps, + "accessibilityLiveRegion", + sourceProps.accessibilityLiveRegion, + AccessibilityLiveRegion::None)), + accessibilityRole(convertRawProp( + context, + rawProps, + "accessibilityRole", + sourceProps.accessibilityRole, + "")), accessibilityHint(convertRawProp( context, rawProps, diff --git a/ReactCommon/react/renderer/components/view/AccessibilityProps.h b/ReactCommon/react/renderer/components/view/AccessibilityProps.h index 3b6d9212b2..53b85874a5 100644 --- a/ReactCommon/react/renderer/components/view/AccessibilityProps.h +++ b/ReactCommon/react/renderer/components/view/AccessibilityProps.h @@ -30,6 +30,10 @@ class AccessibilityProps { AccessibilityTraits accessibilityTraits{AccessibilityTraits::None}; AccessibilityState accessibilityState; std::string accessibilityLabel{""}; + AccessibilityLabelledBy accessibilityLabelledBy{}; + AccessibilityLiveRegion accessibilityLiveRegion{ + AccessibilityLiveRegion::None}; + std::string accessibilityRole{""}; std::string accessibilityHint{""}; AccessibilityValue accessibilityValue; std::vector accessibilityActions{}; diff --git a/ReactCommon/react/renderer/components/view/ViewProps.cpp b/ReactCommon/react/renderer/components/view/ViewProps.cpp index 9c694fd501..5b9cd99e5f 100644 --- a/ReactCommon/react/renderer/components/view/ViewProps.cpp +++ b/ReactCommon/react/renderer/components/view/ViewProps.cpp @@ -137,13 +137,53 @@ ViewProps::ViewProps( rawProps, "removeClippedSubviews", sourceProps.removeClippedSubviews, - false)), + false)) +#ifdef ANDROID + , elevation(convertRawProp( context, rawProps, "elevation", sourceProps.elevation, - {})){}; + {})), + nativeBackground(convertRawProp( + context, + rawProps, + "nativeBackgroundAndroid", + sourceProps.nativeBackground, + {})), + nativeForeground(convertRawProp( + context, + rawProps, + "nativeForegroundAndroid", + sourceProps.nativeForeground, + {})), + focusable(convertRawProp( + context, + rawProps, + "focusable", + sourceProps.focusable, + {})), + hasTVPreferredFocus(convertRawProp( + context, + rawProps, + "hasTVPreferredFocus", + sourceProps.hasTVPreferredFocus, + {})), + needsOffscreenAlphaCompositing(convertRawProp( + context, + rawProps, + "needsOffscreenAlphaCompositing", + sourceProps.needsOffscreenAlphaCompositing, + {})), + renderToHardwareTextureAndroid(convertRawProp( + context, + rawProps, + "renderToHardwareTextureAndroid", + sourceProps.renderToHardwareTextureAndroid, + {})) +#endif + {}; #pragma mark - Convenience Methods diff --git a/ReactCommon/react/renderer/components/view/ViewProps.h b/ReactCommon/react/renderer/components/view/ViewProps.h index 29dfa265eb..497604809b 100644 --- a/ReactCommon/react/renderer/components/view/ViewProps.h +++ b/ReactCommon/react/renderer/components/view/ViewProps.h @@ -69,6 +69,18 @@ class ViewProps : public YogaStylableProps, public AccessibilityProps { Float elevation{}; /* Android-only */ +#ifdef ANDROID + + butter::optional nativeBackground{}; + butter::optional nativeForeground{}; + + bool focusable{false}; + bool hasTVPreferredFocus{false}; + bool needsOffscreenAlphaCompositing{false}; + bool renderToHardwareTextureAndroid{false}; + +#endif + #pragma mark - Convenience Methods BorderMetrics resolveBorderMetrics(LayoutMetrics const &layoutMetrics) const; diff --git a/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp b/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp index bbc05ea771..b23aac9d51 100644 --- a/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp +++ b/ReactCommon/react/renderer/components/view/ViewShadowNode.cpp @@ -35,7 +35,6 @@ void ViewShadowNode::initialize() noexcept { viewProps.pointerEvents == PointerEventsMode::None || !viewProps.nativeId.empty() || viewProps.accessible || viewProps.opacity != 1.0 || viewProps.transform != Transform{} || - viewProps.elevation != 0 || (viewProps.zIndex.has_value() && viewProps.yogaStyle.positionType() != YGPositionTypeStatic) || viewProps.yogaStyle.display() == YGDisplayNone || @@ -46,6 +45,10 @@ void ViewShadowNode::initialize() noexcept { viewProps.importantForAccessibility != ImportantForAccessibility::Auto || viewProps.removeClippedSubviews; +#ifdef ANDROID + formsStackingContext = formsStackingContext || viewProps.elevation != 0; +#endif + bool formsView = formsStackingContext || isColorMeaningful(viewProps.backgroundColor) || isColorMeaningful(viewProps.foregroundColor) || @@ -53,6 +56,14 @@ void ViewShadowNode::initialize() noexcept { !(viewProps.yogaStyle.border() == YGStyle::Edges{}) || !viewProps.testId.empty(); +#ifdef ANDROID + formsView = formsView || viewProps.nativeBackground.has_value() || + viewProps.nativeForeground.has_value() || viewProps.focusable || + viewProps.hasTVPreferredFocus || + viewProps.needsOffscreenAlphaCompositing || + viewProps.renderToHardwareTextureAndroid; +#endif + if (formsView) { traits_.set(ShadowNodeTraits::Trait::FormsView); } else { diff --git a/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h b/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h index 833b7544df..6659e9d710 100644 --- a/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h +++ b/ReactCommon/react/renderer/components/view/accessibilityPropsConversions.h @@ -263,5 +263,38 @@ inline void fromRawValue( } } +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + AccessibilityLabelledBy &result) { + if (value.hasType()) { + result.value.push_back((std::string)value); + } else if (value.hasType>()) { + result.value = (std::vector)value; + } +} + +inline void fromRawValue( + const PropsParserContext &context, + const RawValue &value, + AccessibilityLiveRegion &result) { + react_native_assert(value.hasType()); + if (value.hasType()) { + auto string = (std::string)value; + if (string == "none") { + result = AccessibilityLiveRegion::None; + } else if (string == "polite") { + result = AccessibilityLiveRegion::Polite; + } else if (string == "assertive") { + result = AccessibilityLiveRegion::Assertive; + } else { + LOG(ERROR) << "Unsupported AccessibilityLiveRegion value: " << string; + react_native_assert(false); + } + } else { + LOG(ERROR) << "Unsupported AccessibilityLiveRegion type"; + } +} + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/components/view/primitives.h b/ReactCommon/react/renderer/components/view/primitives.h index 66df8707a0..8c2d0ed8f3 100644 --- a/ReactCommon/react/renderer/components/view/primitives.h +++ b/ReactCommon/react/renderer/components/view/primitives.h @@ -219,5 +219,48 @@ struct BorderMetrics { } }; +#ifdef ANDROID + +struct NativeDrawable { + enum class Kind { + Ripple, + ThemeAttr, + }; + + struct Ripple { + butter::optional color{}; + bool borderless{false}; + butter::optional rippleRadius{}; + + bool operator==(const Ripple &rhs) const { + return std::tie(this->color, this->borderless, this->rippleRadius) == + std::tie(rhs.color, rhs.borderless, rhs.rippleRadius); + } + }; + + Kind kind; + std::string themeAttr; + Ripple ripple; + + bool operator==(const NativeDrawable &rhs) const { + if (this->kind != rhs.kind) + return false; + switch (this->kind) { + case Kind::ThemeAttr: + return this->themeAttr == rhs.themeAttr; + case Kind::Ripple: + return this->ripple == rhs.ripple; + } + } + + bool operator!=(const NativeDrawable &rhs) const { + return !(*this == rhs); + } + + ~NativeDrawable() = default; +}; + +#endif + } // namespace react } // namespace facebook diff --git a/ReactCommon/react/renderer/components/view/propsConversions.h b/ReactCommon/react/renderer/components/view/propsConversions.h index 502b14704b..36d6966a4f 100644 --- a/ReactCommon/react/renderer/components/view/propsConversions.h +++ b/ReactCommon/react/renderer/components/view/propsConversions.h @@ -599,5 +599,58 @@ static inline ViewEvents convertRawProp( return result; } +#ifdef ANDROID + +static inline void fromRawValue( + const PropsParserContext &context, + RawValue const &rawValue, + NativeDrawable &result) { + auto map = (butter::map)rawValue; + + auto typeIterator = map.find("type"); + react_native_assert( + typeIterator != map.end() && typeIterator->second.hasType()); + std::string type = (std::string)typeIterator->second; + + if (type == "ThemeAttrAndroid") { + auto attrIterator = map.find("attribute"); + react_native_assert( + attrIterator != map.end() && + attrIterator->second.hasType()); + + result = NativeDrawable{ + .kind = NativeDrawable::Kind::ThemeAttr, + .themeAttr = (std::string)attrIterator->second, + }; + } else if (type == "RippleAndroid") { + auto color = map.find("color"); + auto borderless = map.find("borderless"); + auto rippleRadius = map.find("rippleRadius"); + + result = NativeDrawable{ + .kind = NativeDrawable::Kind::Ripple, + .ripple = + NativeDrawable::Ripple{ + .color = color != map.end() && color->second.hasType() + ? (int32_t)color->second + : butter::optional{}, + .borderless = borderless != map.end() && + borderless->second.hasType() + ? (bool)borderless->second + : false, + .rippleRadius = rippleRadius != map.end() && + rippleRadius->second.hasType() + ? (Float)rippleRadius->second + : butter::optional{}, + }, + }; + } else { + LOG(ERROR) << "Unknown native drawable type: " << type; + react_native_assert(false); + } +} + +#endif + } // namespace react } // namespace facebook