diff --git a/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js b/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js index d7c0859c3f4b6e..0eba2c14304152 100644 --- a/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js +++ b/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js @@ -341,6 +341,30 @@ const AccessibilityInfo = { } }, + /** + * Post a string to be announced by the screen reader. + * - `announcement`: The string announced by the screen reader. + * - `options`: An object that configures the reading options. + * - `queue`: The announcement will be queued behind existing announcements. iOS only. + */ + announceForAccessibilityWithOptions( + announcement: string, + options: {queue?: boolean}, + ): void { + if (Platform.OS === 'android') { + NativeAccessibilityInfoAndroid?.announceForAccessibility(announcement); + } else { + if (NativeAccessibilityManagerIOS?.announceForAccessibilityWithOptions) { + NativeAccessibilityManagerIOS?.announceForAccessibilityWithOptions( + announcement, + options, + ); + } else { + NativeAccessibilityManagerIOS?.announceForAccessibility(announcement); + } + } + }, + /** * @deprecated Use `remove` on the EventSubscription from `addEventListener`. */ diff --git a/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager.js b/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager.js index 9ee2403e372b91..78dc83170ad3ca 100644 --- a/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager.js +++ b/Libraries/Components/AccessibilityInfo/NativeAccessibilityManager.js @@ -52,6 +52,10 @@ export interface Spec extends TurboModule { |}) => void; +setAccessibilityFocus: (reactTag: number) => void; +announceForAccessibility: (announcement: string) => void; + +announceForAccessibilityWithOptions?: ( + announcement: string, + options: {queue?: boolean}, + ) => void; } export default (TurboModuleRegistry.get('AccessibilityManager'): ?Spec); diff --git a/React/CoreModules/RCTAccessibilityManager.mm b/React/CoreModules/RCTAccessibilityManager.mm index 3d0c7765d059ca..29e2fcbeddae1d 100644 --- a/React/CoreModules/RCTAccessibilityManager.mm +++ b/React/CoreModules/RCTAccessibilityManager.mm @@ -301,6 +301,28 @@ static void setMultipliers( UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcement); } +RCT_EXPORT_METHOD(announceForAccessibilityWithOptions + : (NSString *)announcement options + : (JS::NativeAccessibilityManager::SpecAnnounceForAccessibilityWithOptionsOptions &)options) +{ + if (@available(iOS 11.0, *)) { + NSMutableDictionary *attrsDictionary = [NSMutableDictionary new]; + if (options.queue()) { + attrsDictionary[UIAccessibilitySpeechAttributeQueueAnnouncement] = @(*(options.queue()) ? YES : NO); + } + + if (attrsDictionary.count > 0) { + NSAttributedString *announcementWithAttrs = [[NSAttributedString alloc] initWithString:announcement + attributes:attrsDictionary]; + UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcementWithAttrs); + } else { + UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcement); + } + } else { + UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcement); + } +} + RCT_EXPORT_METHOD(getMultiplier : (RCTResponseSenderBlock)callback) { if (callback) { diff --git a/packages/rn-tester/Podfile.lock b/packages/rn-tester/Podfile.lock index 518392bdf56f6d..756549efd02246 100644 --- a/packages/rn-tester/Podfile.lock +++ b/packages/rn-tester/Podfile.lock @@ -881,7 +881,7 @@ SPEC CHECKSUMS: CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 DoubleConversion: 831926d9b8bf8166fd87886c4abab286c2422662 FBLazyVector: b81a2b70c72d8b0aefb652cea22c11e9ffd02949 - FBReactNativeSpec: 755b7fee1b08aefd74fb2fa9f7312b253719d536 + FBReactNativeSpec: 37e065c0cfc5da966014bf62b50edb066d8206cd Flipper: 30e8eeeed6abdc98edaf32af0cda2f198be4b733 Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c Flipper-DoubleConversion: 57ffbe81ef95306cc9e69c4aa3aeeeeb58a6a28c @@ -923,10 +923,10 @@ SPEC CHECKSUMS: React-RCTTest: 12bbd7fc2e72bd9920dc7286c5b8ef96639582b6 React-RCTText: e9146b2c0550a83d1335bfe2553760070a2d75c7 React-RCTVibration: 50be9c390f2da76045ef0dfdefa18b9cf9f35cfa - React-rncore: d09af3a25cbff0b484776785676c28f3729e07f5 + React-rncore: c57d93f56e2d385bdbda34eae2d20d4d3c0c8b4a React-runtimeexecutor: 4b0c6eb341c7d3ceb5e2385cb0fdb9bf701024f3 ReactCommon: 7a2714d1128f965392b6f99a8b390e3aa38c9569 - ScreenshotManager: e8a3fc9b2e24b81127b36cb4ebe0eed65090c949 + ScreenshotManager: 9f69049876d8aafafa13a1a635baa8f7e168eee4 Yoga: c0d06f5380d34e939f55420669a60fe08b79bd75 YogaKit: f782866e155069a2cca2517aafea43200b01fd5a diff --git a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js index b794565169c1c9..eb25ee596d3c6e 100644 --- a/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js +++ b/packages/rn-tester/js/examples/Accessibility/AccessibilityExample.js @@ -864,10 +864,65 @@ class FakeSliderExample extends React.Component<{}, FakeSliderExampleState> { class AnnounceForAccessibility extends React.Component<{}> { _handleOnPress = () => - AccessibilityInfo.announceForAccessibility('Announcement Test'); + setTimeout( + () => AccessibilityInfo.announceForAccessibility('Announcement Test'), + 1000, + ); + + _handleOnPressQueued = () => + setTimeout( + () => + AccessibilityInfo.announceForAccessibilityWithOptions( + 'Queued Announcement Test', + {queue: true}, + ), + 1000, + ); + + _handleOnPressQueueMultiple = () => { + setTimeout( + () => + AccessibilityInfo.announceForAccessibilityWithOptions( + 'First Queued Announcement Test', + {queue: true}, + ), + 1000, + ); + setTimeout( + () => + AccessibilityInfo.announceForAccessibilityWithOptions( + 'Second Queued Announcement Test', + {queue: true}, + ), + 1100, + ); + setTimeout( + () => + AccessibilityInfo.announceForAccessibilityWithOptions( + 'Third Queued Announcement Test', + {queue: true}, + ), + 1200, + ); + }; render(): React.Node { - return ( + return Platform.OS === 'ios' ? ( + +