mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
d8931e2f94
Summary: Changelog: [Internal] https://fb.workplace.com/groups/rn.support/posts/6677051292343429 ax team is building a tool to extract information about the views for design reviewers, and RN has some AX information that is not working atm because of dependency on whether voiceover is on or not. so, this will give them the ability to programmatically set that field and hopefully be able to get accurate ax info Reviewed By: ikenwoo Differential Revision: D31010566 fbshipit-source-id: 4c8a33fce40266b270dd5994442c8472ca88f5dd
383 lines
15 KiB
Plaintext
383 lines
15 KiB
Plaintext
/*
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*/
|
|
|
|
#import "RCTAccessibilityManager.h"
|
|
#import "RCTAccessibilityManager+Internal.h"
|
|
|
|
#import <FBReactNativeSpec/FBReactNativeSpec.h>
|
|
#import <React/RCTBridge.h>
|
|
#import <React/RCTConvert.h>
|
|
#import <React/RCTEventDispatcherProtocol.h>
|
|
#import <React/RCTLog.h>
|
|
#import <React/RCTUIManager.h>
|
|
|
|
#import "CoreModulesPlugins.h"
|
|
|
|
NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification =
|
|
@"RCTAccessibilityManagerDidUpdateMultiplierNotification";
|
|
|
|
@interface RCTAccessibilityManager () <NativeAccessibilityManagerSpec>
|
|
|
|
@property (nonatomic, copy) NSString *contentSizeCategory;
|
|
@property (nonatomic, assign) CGFloat multiplier;
|
|
|
|
@end
|
|
|
|
@implementation RCTAccessibilityManager
|
|
|
|
@synthesize viewRegistry_DEPRECATED = _viewRegistry_DEPRECATED;
|
|
@synthesize moduleRegistry = _moduleRegistry;
|
|
@synthesize multipliers = _multipliers;
|
|
|
|
RCT_EXPORT_MODULE()
|
|
|
|
+ (BOOL)requiresMainQueueSetup
|
|
{
|
|
return YES;
|
|
}
|
|
|
|
- (instancetype)init
|
|
{
|
|
if (self = [super init]) {
|
|
_multiplier = 1.0;
|
|
|
|
// TODO: can this be moved out of the startup path?
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(didReceiveNewContentSizeCategory:)
|
|
name:UIContentSizeCategoryDidChangeNotification
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(accessibilityAnnouncementDidFinish:)
|
|
name:UIAccessibilityAnnouncementDidFinishNotification
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(boldTextStatusDidChange:)
|
|
name:UIAccessibilityBoldTextStatusDidChangeNotification
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(grayscaleStatusDidChange:)
|
|
name:UIAccessibilityGrayscaleStatusDidChangeNotification
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(invertColorsStatusDidChange:)
|
|
name:UIAccessibilityInvertColorsStatusDidChangeNotification
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(reduceMotionStatusDidChange:)
|
|
name:UIAccessibilityReduceMotionStatusDidChangeNotification
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(reduceTransparencyStatusDidChange:)
|
|
name:UIAccessibilityReduceTransparencyStatusDidChangeNotification
|
|
object:nil];
|
|
|
|
[[NSNotificationCenter defaultCenter] addObserver:self
|
|
selector:@selector(voiceVoiceOverStatusDidChange:)
|
|
name:UIAccessibilityVoiceOverStatusDidChangeNotification
|
|
object:nil];
|
|
|
|
self.contentSizeCategory = RCTSharedApplication().preferredContentSizeCategory;
|
|
_isBoldTextEnabled = UIAccessibilityIsBoldTextEnabled();
|
|
_isGrayscaleEnabled = UIAccessibilityIsGrayscaleEnabled();
|
|
_isInvertColorsEnabled = UIAccessibilityIsInvertColorsEnabled();
|
|
_isReduceMotionEnabled = UIAccessibilityIsReduceMotionEnabled();
|
|
_isReduceTransparencyEnabled = UIAccessibilityIsReduceTransparencyEnabled();
|
|
_isVoiceOverEnabled = UIAccessibilityIsVoiceOverRunning();
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (void)didReceiveNewContentSizeCategory:(NSNotification *)note
|
|
{
|
|
self.contentSizeCategory = note.userInfo[UIContentSizeCategoryNewValueKey];
|
|
}
|
|
|
|
- (void)accessibilityAnnouncementDidFinish:(__unused NSNotification *)notification
|
|
{
|
|
NSDictionary *userInfo = notification.userInfo;
|
|
// Response dictionary to populate the event with.
|
|
NSDictionary *response = @{
|
|
@"announcement" : userInfo[UIAccessibilityAnnouncementKeyStringValue],
|
|
@"success" : userInfo[UIAccessibilityAnnouncementKeyWasSuccessful]
|
|
};
|
|
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"announcementFinished" body:response];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
|
|
- (void)boldTextStatusDidChange:(__unused NSNotification *)notification
|
|
{
|
|
BOOL newBoldTextEnabled = UIAccessibilityIsBoldTextEnabled();
|
|
if (_isBoldTextEnabled != newBoldTextEnabled) {
|
|
_isBoldTextEnabled = newBoldTextEnabled;
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"boldTextChanged"
|
|
body:@(_isBoldTextEnabled)];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
|
|
- (void)grayscaleStatusDidChange:(__unused NSNotification *)notification
|
|
{
|
|
BOOL newGrayscaleEnabled = UIAccessibilityIsGrayscaleEnabled();
|
|
if (_isGrayscaleEnabled != newGrayscaleEnabled) {
|
|
_isGrayscaleEnabled = newGrayscaleEnabled;
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"grayscaleChanged"
|
|
body:@(_isGrayscaleEnabled)];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
|
|
- (void)invertColorsStatusDidChange:(__unused NSNotification *)notification
|
|
{
|
|
BOOL newInvertColorsEnabled = UIAccessibilityIsInvertColorsEnabled();
|
|
if (_isInvertColorsEnabled != newInvertColorsEnabled) {
|
|
_isInvertColorsEnabled = newInvertColorsEnabled;
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"invertColorsChanged"
|
|
body:@(_isInvertColorsEnabled)];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
|
|
- (void)reduceMotionStatusDidChange:(__unused NSNotification *)notification
|
|
{
|
|
BOOL newReduceMotionEnabled = UIAccessibilityIsReduceMotionEnabled();
|
|
if (_isReduceMotionEnabled != newReduceMotionEnabled) {
|
|
_isReduceMotionEnabled = newReduceMotionEnabled;
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"reduceMotionChanged"
|
|
body:@(_isReduceMotionEnabled)];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
|
|
- (void)reduceTransparencyStatusDidChange:(__unused NSNotification *)notification
|
|
{
|
|
BOOL newReduceTransparencyEnabled = UIAccessibilityIsReduceTransparencyEnabled();
|
|
if (_isReduceTransparencyEnabled != newReduceTransparencyEnabled) {
|
|
_isReduceTransparencyEnabled = newReduceTransparencyEnabled;
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"reduceTransparencyChanged"
|
|
body:@(_isReduceTransparencyEnabled)];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
|
|
- (void)voiceVoiceOverStatusDidChange:(__unused NSNotification *)notification
|
|
{
|
|
BOOL isVoiceOverEnabled = UIAccessibilityIsVoiceOverRunning();
|
|
[self _setIsVoiceOverEnabled:isVoiceOverEnabled];
|
|
}
|
|
|
|
- (void)_setIsVoiceOverEnabled:(BOOL)isVoiceOverEnabled
|
|
{
|
|
if (_isVoiceOverEnabled != isVoiceOverEnabled) {
|
|
_isVoiceOverEnabled = isVoiceOverEnabled;
|
|
#pragma clang diagnostic push
|
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
|
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"screenReaderChanged"
|
|
body:@(_isVoiceOverEnabled)];
|
|
#pragma clang diagnostic pop
|
|
}
|
|
}
|
|
|
|
- (void)setContentSizeCategory:(NSString *)contentSizeCategory
|
|
{
|
|
if (_contentSizeCategory != contentSizeCategory) {
|
|
_contentSizeCategory = [contentSizeCategory copy];
|
|
[self invalidateMultiplier];
|
|
}
|
|
}
|
|
|
|
- (void)invalidateMultiplier
|
|
{
|
|
self.multiplier = [self multiplierForContentSizeCategory:_contentSizeCategory];
|
|
[[NSNotificationCenter defaultCenter] postNotificationName:RCTAccessibilityManagerDidUpdateMultiplierNotification
|
|
object:self];
|
|
}
|
|
|
|
- (CGFloat)multiplierForContentSizeCategory:(NSString *)category
|
|
{
|
|
NSNumber *m = self.multipliers[category];
|
|
if (m.doubleValue <= 0.0) {
|
|
RCTLogError(@"Can't determine multiplier for category %@. Using 1.0.", category);
|
|
m = @1.0;
|
|
}
|
|
return m.doubleValue;
|
|
}
|
|
|
|
- (void)setMultipliers:(NSDictionary<NSString *, NSNumber *> *)multipliers
|
|
{
|
|
if (_multipliers != multipliers) {
|
|
_multipliers = [multipliers copy];
|
|
[self invalidateMultiplier];
|
|
}
|
|
}
|
|
|
|
- (NSDictionary<NSString *, NSNumber *> *)multipliers
|
|
{
|
|
if (_multipliers == nil) {
|
|
_multipliers = @{
|
|
UIContentSizeCategoryExtraSmall : @0.823,
|
|
UIContentSizeCategorySmall : @0.882,
|
|
UIContentSizeCategoryMedium : @0.941,
|
|
UIContentSizeCategoryLarge : @1.0,
|
|
UIContentSizeCategoryExtraLarge : @1.118,
|
|
UIContentSizeCategoryExtraExtraLarge : @1.235,
|
|
UIContentSizeCategoryExtraExtraExtraLarge : @1.353,
|
|
UIContentSizeCategoryAccessibilityMedium : @1.786,
|
|
UIContentSizeCategoryAccessibilityLarge : @2.143,
|
|
UIContentSizeCategoryAccessibilityExtraLarge : @2.643,
|
|
UIContentSizeCategoryAccessibilityExtraExtraLarge : @3.143,
|
|
UIContentSizeCategoryAccessibilityExtraExtraExtraLarge : @3.571
|
|
};
|
|
}
|
|
return _multipliers;
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(setAccessibilityContentSizeMultipliers
|
|
: (JS::NativeAccessibilityManager::SpecSetAccessibilityContentSizeMultipliersJSMultipliers &)
|
|
JSMultipliers)
|
|
{
|
|
NSMutableDictionary<NSString *, NSNumber *> *multipliers = [NSMutableDictionary new];
|
|
setMultipliers(multipliers, UIContentSizeCategoryExtraSmall, JSMultipliers.extraSmall());
|
|
setMultipliers(multipliers, UIContentSizeCategorySmall, JSMultipliers.small());
|
|
setMultipliers(multipliers, UIContentSizeCategoryMedium, JSMultipliers.medium());
|
|
setMultipliers(multipliers, UIContentSizeCategoryLarge, JSMultipliers.large());
|
|
setMultipliers(multipliers, UIContentSizeCategoryExtraLarge, JSMultipliers.extraLarge());
|
|
setMultipliers(multipliers, UIContentSizeCategoryExtraExtraLarge, JSMultipliers.extraExtraLarge());
|
|
setMultipliers(multipliers, UIContentSizeCategoryExtraExtraExtraLarge, JSMultipliers.extraExtraExtraLarge());
|
|
setMultipliers(multipliers, UIContentSizeCategoryAccessibilityMedium, JSMultipliers.accessibilityMedium());
|
|
setMultipliers(multipliers, UIContentSizeCategoryAccessibilityLarge, JSMultipliers.accessibilityLarge());
|
|
setMultipliers(multipliers, UIContentSizeCategoryAccessibilityExtraLarge, JSMultipliers.accessibilityExtraLarge());
|
|
setMultipliers(
|
|
multipliers, UIContentSizeCategoryAccessibilityExtraExtraLarge, JSMultipliers.accessibilityExtraExtraLarge());
|
|
setMultipliers(
|
|
multipliers,
|
|
UIContentSizeCategoryAccessibilityExtraExtraExtraLarge,
|
|
JSMultipliers.accessibilityExtraExtraExtraLarge());
|
|
self.multipliers = multipliers;
|
|
}
|
|
|
|
static void setMultipliers(
|
|
NSMutableDictionary<NSString *, NSNumber *> *multipliers,
|
|
NSString *key,
|
|
folly::Optional<double> optionalDouble)
|
|
{
|
|
if (optionalDouble.hasValue()) {
|
|
multipliers[key] = @(optionalDouble.value());
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(setAccessibilityFocus : (double)reactTag)
|
|
{
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
UIView *view = [self.viewRegistry_DEPRECATED viewForReactTag:@(reactTag)];
|
|
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, view);
|
|
});
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(announceForAccessibility : (NSString *)announcement)
|
|
{
|
|
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, announcement);
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getMultiplier : (RCTResponseSenderBlock)callback)
|
|
{
|
|
if (callback) {
|
|
callback(@[ @(self.multiplier) ]);
|
|
}
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getCurrentBoldTextState
|
|
: (RCTResponseSenderBlock)onSuccess onError
|
|
: (__unused RCTResponseSenderBlock)onError)
|
|
{
|
|
onSuccess(@[ @(_isBoldTextEnabled) ]);
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getCurrentGrayscaleState
|
|
: (RCTResponseSenderBlock)onSuccess onError
|
|
: (__unused RCTResponseSenderBlock)onError)
|
|
{
|
|
onSuccess(@[ @(_isGrayscaleEnabled) ]);
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getCurrentInvertColorsState
|
|
: (RCTResponseSenderBlock)onSuccess onError
|
|
: (__unused RCTResponseSenderBlock)onError)
|
|
{
|
|
onSuccess(@[ @(_isInvertColorsEnabled) ]);
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getCurrentReduceMotionState
|
|
: (RCTResponseSenderBlock)onSuccess onError
|
|
: (__unused RCTResponseSenderBlock)onError)
|
|
{
|
|
onSuccess(@[ @(_isReduceMotionEnabled) ]);
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getCurrentReduceTransparencyState
|
|
: (RCTResponseSenderBlock)onSuccess onError
|
|
: (__unused RCTResponseSenderBlock)onError)
|
|
{
|
|
onSuccess(@[ @(_isReduceTransparencyEnabled) ]);
|
|
}
|
|
|
|
RCT_EXPORT_METHOD(getCurrentVoiceOverState
|
|
: (RCTResponseSenderBlock)onSuccess onError
|
|
: (__unused RCTResponseSenderBlock)onError)
|
|
{
|
|
onSuccess(@[ @(_isVoiceOverEnabled) ]);
|
|
}
|
|
|
|
- (std::shared_ptr<facebook::react::TurboModule>)getTurboModule:
|
|
(const facebook::react::ObjCTurboModule::InitParams &)params
|
|
{
|
|
return std::make_shared<facebook::react::NativeAccessibilityManagerSpecJSI>(params);
|
|
}
|
|
|
|
#pragma mark - Internal
|
|
|
|
void RCTAccessibilityManagerSetIsVoiceOverEnabled(
|
|
RCTAccessibilityManager *accessibilityManager,
|
|
BOOL isVoiceOverEnabled)
|
|
{
|
|
[accessibilityManager _setIsVoiceOverEnabled:isVoiceOverEnabled];
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation RCTBridge (RCTAccessibilityManager)
|
|
|
|
- (RCTAccessibilityManager *)accessibilityManager
|
|
{
|
|
return [self moduleForClass:[RCTAccessibilityManager class]];
|
|
}
|
|
|
|
@end
|
|
|
|
Class RCTAccessibilityManagerCls(void)
|
|
{
|
|
return RCTAccessibilityManager.class;
|
|
}
|