From 74661c0e496ae2f5e72d24a04b340d12ca947ddb Mon Sep 17 00:00:00 2001 From: Andrew Coates Date: Wed, 26 Aug 2020 11:26:56 -0700 Subject: [PATCH 1/7] Initial AccessibilityInfo implementation --- .../Microsoft.ReactNative.vcxproj | 2 + .../Microsoft.ReactNative.vcxproj.filters | 6 ++ .../Modules/AccessibilityInfoModule.cpp | 78 +++++++++++++++++++ .../Modules/AccessibilityInfoModule.h | 30 +++++++ .../ReactHost/ReactInstanceWin.cpp | 8 ++ .../Accessibility/AccessibilityInfoExample.js | 55 +++++++++++++ .../RNTester/js/utils/RNTesterList.windows.ts | 4 + 7 files changed, 183 insertions(+) create mode 100644 vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.cpp create mode 100644 vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.h create mode 100644 vnext/src/RNTester/js/examples/Accessibility/AccessibilityInfoExample.js diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj index b0b496a9545..b4c20caeaa0 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj @@ -204,6 +204,7 @@ IJSValueWriter.idl + @@ -407,6 +408,7 @@ IJSValueWriter.idl + diff --git a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters index 9c11569c47c..4270a024341 100644 --- a/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters +++ b/vnext/Microsoft.ReactNative/Microsoft.ReactNative.vcxproj.filters @@ -65,6 +65,9 @@ CxxReactUWP + + Modules + Modules\Animated @@ -404,6 +407,9 @@ Base + + Modules + Modules\Animated diff --git a/vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.cpp b/vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.cpp new file mode 100644 index 00000000000..de9fb7db5d1 --- /dev/null +++ b/vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.cpp @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#include "pch.h" +#include "AccessibilityInfoModule.h" +#include +#include +#include +#include +#include +#include "Unicode.h" +#include "Utils/Helpers.h" + +namespace Microsoft::ReactNative { + +void AccessibilityInfo::Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept { + m_context = reactContext; +} + +void AccessibilityInfo::isReduceMotionEnabled(std::function const & onSuccess) noexcept { + + auto jsDispatcher = m_context.JSDispatcher(); + m_context.UIDispatcher().Post([weakThis = weak_from_this(), jsDispatcher, onSuccess] { + if (auto strongThis = weakThis.lock()) { + winrt::Windows::UI::ViewManagement::UISettings uiSettings; + auto animationsEnabled = uiSettings.AnimationsEnabled(); + jsDispatcher.Post([animationsEnabled, onSuccess] { onSuccess(!animationsEnabled); }); + } + }); +} + +void AccessibilityInfo::isTouchExplorationEnabled(std::function const & onSuccess) noexcept { + onSuccess(false); +} + +void AccessibilityInfo::setAccessibilityFocus(double reactTag) noexcept { + // no-op +} + +void AccessibilityInfo::announceForAccessibility(std::string announcement) noexcept { + + m_context.UIDispatcher().Post([context = m_context, announcement = std::move(announcement)] { + xaml::UIElement element{nullptr}; + + // Windows requires a specific element to announce from. Unfortunately the react-native API does not provide a tag + // So we need to find something to try to raise the notification event from + + if (auto window = winrt::Windows::UI::Xaml::Window::Current()) { + element = window.Content(); + } + + if (!element && react::uwp::Is19H1OrHigher()) { + // XamlRoot added in 19H1 + if (auto xamlRoot = React::XamlUIService::GetXamlRoot(context.Properties().Handle())) { + element = xamlRoot.Content(); + } + } + + if (!element) { + return; + } + + auto peer = xaml::Automation::Peers::FrameworkElementAutomationPeer::FromElement(element); + + if (!peer) { + return; + } + + winrt::hstring hstr{Microsoft::Common::Unicode::Utf8ToUtf16(announcement)}; + peer.RaiseNotificationEvent( + xaml::Automation::Peers::AutomationNotificationKind::Other, + xaml::Automation::Peers::AutomationNotificationProcessing::ImportantMostRecent, + hstr, + hstr); + }); +} + +} // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.h b/vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.h new file mode 100644 index 00000000000..45360caa06c --- /dev/null +++ b/vnext/Microsoft.ReactNative/Modules/AccessibilityInfoModule.h @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#pragma once + +#include + +namespace Microsoft::ReactNative { + +REACT_MODULE(AccessibilityInfo) +struct AccessibilityInfo : public std::enable_shared_from_this { + REACT_INIT(Initialize) + void Initialize(winrt::Microsoft::ReactNative::ReactContext const &reactContext) noexcept; + + REACT_METHOD(isReduceMotionEnabled) + void isReduceMotionEnabled(std::function const & onSuccess) noexcept; + + REACT_METHOD(isTouchExplorationEnabled) + void isTouchExplorationEnabled(std::function const & onSuccess) noexcept; + + REACT_METHOD(setAccessibilityFocus) + void setAccessibilityFocus(double reactTag) noexcept; + + REACT_METHOD(announceForAccessibility) + void announceForAccessibility(std::string announcement) noexcept; + + private: + React::ReactContext m_context; +}; + +} // namespace Microsoft::ReactNative diff --git a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp index 5f8a2044490..0c295e4c022 100644 --- a/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp +++ b/vnext/Microsoft.ReactNative/ReactHost/ReactInstanceWin.cpp @@ -14,6 +14,7 @@ #include "Microsoft.ReactNative/IReactNotificationService.h" #include "Microsoft.ReactNative/Threading/MessageQueueThreadFactory.h" +#include "../../codegen/NativeAccessibilityInfoSpec.g.h" #include "../../codegen/NativeAppStateSpec.g.h" #include "../../codegen/NativeClipboardSpec.g.h" #include "../../codegen/NativeDevSettingsSpec.g.h" @@ -31,6 +32,7 @@ #include "DevMenu.h" #include "IReactContext.h" #include "IReactDispatcher.h" +#include "Modules/AccessibilityInfoModule.h" #include "Modules/AlertModule.h" #include "Modules/AppStateModule.h" #include "Modules/ClipboardModule.h" @@ -150,6 +152,12 @@ void ReactInstanceWin::LoadModules( turboModulesProvider->AddModuleProvider(name, provider); } }; + + registerTurboModule( + L"AccessibilityInfo", + winrt::Microsoft::ReactNative::MakeTurboModuleProvider< + ::Microsoft::ReactNative::AccessibilityInfo, + ::Microsoft::ReactNativeSpecs::AccessibilityInfoSpec>()); registerTurboModule(L"Alert", winrt::Microsoft::ReactNative::MakeModuleProvider<::Microsoft::ReactNative::Alert>()); diff --git a/vnext/src/RNTester/js/examples/Accessibility/AccessibilityInfoExample.js b/vnext/src/RNTester/js/examples/Accessibility/AccessibilityInfoExample.js new file mode 100644 index 00000000000..2d740dab0f5 --- /dev/null +++ b/vnext/src/RNTester/js/examples/Accessibility/AccessibilityInfoExample.js @@ -0,0 +1,55 @@ +/** + * Copyright (c) Microsoft Corporation. + * Licensed under the MIT License. + * @format + */ + +'use strict'; + +const React = require('react'); +const {AccessibilityInfo, Button, Text, View} = require('react-native'); + +function announceSomething() { + AccessibilityInfo.announceForAccessibility('Something!'); +} + +class AccessibilityInfoExample extends React.Component { + state = { + isReduceMotionEnabled: 'unknown', + isScreenReaderEnabled: 'unknown', + }; + + componentDidMount() { + AccessibilityInfo.isReduceMotionEnabled().done(isEnabled => { + this.setState({isReduceMotionEnabled: isEnabled}); + }); + AccessibilityInfo.isScreenReaderEnabled().done(isEnabled => { + this.setState({isScreenReaderEnabled: isEnabled}); + }); + } + + render() { + return ( + + {`AccessibilityInfo.isReduceMotionEnabled: ${ + this.state.isReduceMotionEnabled + }`} + {`AccessibilityInfo.isScreenReaderEnabled: ${ + this.state.isScreenReaderEnabled + }`} +