Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: unify platform specific high contrast APIs to AccessibilityInfo.isHighContrastEnabled #49965

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ type AccessibilityChangeEventName =
| 'highTextContrastChanged' // Android-only Event
| 'darkerSystemColorsChanged' // iOS-only Event
| 'screenReaderChanged'
| 'reduceTransparencyChanged'; // iOS-only Event
| 'reduceTransparencyChanged' // iOS-only Event
| 'highContrastChanged';

type AccessibilityChangeEvent = boolean;

Expand Down Expand Up @@ -85,6 +86,15 @@ export interface AccessibilityInfoStatic {
*/
isDarkerSystemColorsEnabled: () => Promise<boolean>;

/**
*
* Query whether high contrast is currently enabled.
* On Android, this maps to the API `highTextContrastEnabled`
* On iOS, this maps to the API `darkerSystemColorsEnabled`
*
*/
isHighContrastEnabled: () => Promise<boolean>;

/**
* Query whether reduce motion and prefer cross-fade transitions settings are currently enabled.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
* @format
*/

import type {HostInstance} from '../../../src/private/types/HostInstance';

Check warning on line 11 in packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js

View workflow job for this annotation

GitHub Actions / test_js (22)

Requires should be sorted alphabetically

Check warning on line 11 in packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js

View workflow job for this annotation

GitHub Actions / test_js (20)

Requires should be sorted alphabetically

Check warning on line 11 in packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js

View workflow job for this annotation

GitHub Actions / test_js (18)

Requires should be sorted alphabetically
import type {EventSubscription} from '../../vendor/emitter/EventEmitter';

import RCTDeviceEventEmitter from '../../EventEmitter/RCTDeviceEventEmitter';
Expand All @@ -18,6 +18,8 @@
import NativeAccessibilityInfoAndroid from './NativeAccessibilityInfo';
import NativeAccessibilityManagerIOS from './NativeAccessibilityManager';

import warnOnce from '../../Utilities/warnOnce';

// Events that are only supported on Android.
type AccessibilityEventDefinitionsAndroid = {
accessibilityServiceChanged: [boolean],
Expand All @@ -40,6 +42,7 @@
change: [boolean], // screenReaderChanged
reduceMotionChanged: [boolean],
screenReaderChanged: [boolean],
highContrastChanged: [boolean],
};

type AccessibilityEventTypes = 'click' | 'focus' | 'viewHoverEnter';
Expand All @@ -57,6 +60,7 @@
['accessibilityServiceChanged', 'accessibilityServiceDidChange'],
['invertColorsChanged', 'invertColorDidChange'],
['grayscaleChanged', 'grayscaleModeDidChange'],
['highContrastChanged', 'highContrastDidChange'],
])
: new Map([
['announcementFinished', 'announcementFinished'],
Expand All @@ -68,6 +72,7 @@
['reduceTransparencyChanged', 'reduceTransparencyChanged'],
['screenReaderChanged', 'screenReaderChanged'],
['darkerSystemColorsChanged', 'darkerSystemColorsChanged'],
['highContrastChanged', 'highContrastChanged'],
]);

/**
Expand Down Expand Up @@ -203,6 +208,10 @@
* The result is `true` when high text contrast is enabled and `false` otherwise.
*/
isHighTextContrastEnabled(): Promise<boolean> {
warnOnce(
'isHighTextContrastEnabled-deprecated',
'isHighTextContrastEnabled is deprecated. Use isHighContrastEnabled instead.',
);
return new Promise((resolve, reject) => {
if (Platform.OS === 'android') {
if (NativeAccessibilityInfoAndroid?.isHighTextContrastEnabled != null) {
Expand All @@ -223,21 +232,57 @@
* The result is `true` when dark system colors is enabled and `false` otherwise.
*/
isDarkerSystemColorsEnabled(): Promise<boolean> {
warnOnce(
'isDarkerSystemColorsEnabled-deprecated',
'isDarkerSystemColorsEnabled is deprecated. Use isHighContrastEnabled instead.',
);
return new Promise((resolve, reject) => {
if (Platform.OS === 'android') {
return Promise.resolve(false);
} else {
if (
NativeAccessibilityManagerIOS?.getCurrentDarkerSystemColorsState !=
null
NativeAccessibilityManagerIOS?.getCurrentHighContrastState != null
) {
NativeAccessibilityManagerIOS.getCurrentHighContrastState(
resolve,
reject,
);
} else {
reject(null);
}
}
});
},

/**
* Query whether high contrast is currently enabled.
* On iOS this maps to the API "darkerSystemColorsEnabled"
* On Android this maps to the API "highTextContrastEnabled"
*
* Returns a promise which resolves to a boolean.
* The result is `true` when dark system colors is enabled and `false` otherwise.
*/
isHighContrastEnabled: function (): Promise<boolean> {
return new Promise((resolve, reject) => {
if (Platform.OS === 'android') {
if (NativeAccessibilityInfoAndroid?.isHighTextContrastEnabled != null) {
NativeAccessibilityInfoAndroid.isHighTextContrastEnabled(resolve);
} else {
reject(null);
}
} else if (Platform.OS === 'ios') {
if (
NativeAccessibilityManagerIOS?.getCurrentHighContrastState != null
) {
NativeAccessibilityManagerIOS.getCurrentDarkerSystemColorsState(
NativeAccessibilityManagerIOS.getCurrentHighContrastState(
resolve,
reject,
);
} else {
reject(null);
}
} else {
return Promise.resolve(false);
}
});
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1377,6 +1377,7 @@ type AccessibilityEventDefinitions = {
change: [boolean],
reduceMotionChanged: [boolean],
screenReaderChanged: [boolean],
highContrastChanged: [boolean],
};
type AccessibilityEventTypes = \\"click\\" | \\"focus\\" | \\"viewHoverEnter\\";
declare const AccessibilityInfo: {
Expand All @@ -1386,6 +1387,7 @@ declare const AccessibilityInfo: {
isReduceMotionEnabled(): Promise<boolean>,
isHighTextContrastEnabled(): Promise<boolean>,
isDarkerSystemColorsEnabled(): Promise<boolean>,
isHighContrastEnabled: () => Promise<boolean>,
prefersCrossFadeTransitions(): Promise<boolean>,
isReduceTransparencyEnabled(): Promise<boolean>,
isScreenReaderEnabled(): Promise<boolean>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ NS_ASSUME_NONNULL_BEGIN
@property (readonly, nonatomic) BOOL isGrayscaleEnabled;
@property (readonly, nonatomic) BOOL isInvertColorsEnabled;
@property (readonly, nonatomic) BOOL isReduceMotionEnabled;
@property (readonly, nonatomic) BOOL isDarkerSystemColorsEnabled;
@property (readonly, nonatomic) BOOL isHighContrastEnabled;
@property (readonly, nonatomic) BOOL prefersCrossFadeTransitions;
@property (readonly, nonatomic) BOOL isReduceTransparencyEnabled;
@property (readonly, nonatomic) BOOL isVoiceOverEnabled;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ @implementation RCTInitialAccessibilityValuesProxy {
BOOL _isGrayscaleEnabled;
BOOL _isInvertColorsEnabled;
BOOL _isReduceMotionEnabled;
BOOL _isDarkerSystemColorsEnabled;
BOOL _isHighContrastEnabled;
BOOL _isReduceTransparencyEnabled;
BOOL _isVoiceOverEnabled;
UIContentSizeCategory _preferredContentSizeCategory;
Expand Down Expand Up @@ -100,21 +100,21 @@ - (BOOL)isReduceMotionEnabled
return isReduceMotionEnabled;
}

- (BOOL)isDarkerSystemColorsEnabled
- (BOOL)isHighContrastEnabled
{
{
std::lock_guard<std::mutex> lock(_mutex);
if (_hasRecordedInitialAccessibilityValues) {
return _isDarkerSystemColorsEnabled;
return _isHighContrastEnabled;
}
}

__block BOOL isDarkerSystemColorsEnabled;
__block BOOL isHighContrastEnabled;
RCTUnsafeExecuteOnMainQueueSync(^{
isDarkerSystemColorsEnabled = UIAccessibilityDarkerSystemColorsEnabled();
_isHighContrastEnabled = UIAccessibilityDarkerSystemColorsEnabled();
});

return isDarkerSystemColorsEnabled;
return _isHighContrastEnabled;
}

- (BOOL)isReduceTransparencyEnabled
Expand Down Expand Up @@ -176,7 +176,7 @@ - (void)recordAccessibilityValues
_isGrayscaleEnabled = UIAccessibilityIsGrayscaleEnabled();
_isInvertColorsEnabled = UIAccessibilityIsInvertColorsEnabled();
_isReduceMotionEnabled = UIAccessibilityIsReduceMotionEnabled();
_isDarkerSystemColorsEnabled = UIAccessibilityDarkerSystemColorsEnabled();
_isHighContrastEnabled = UIAccessibilityDarkerSystemColorsEnabled();
_isReduceTransparencyEnabled = UIAccessibilityIsReduceTransparencyEnabled();
_isVoiceOverEnabled = UIAccessibilityIsVoiceOverRunning();
_preferredContentSizeCategory = RCTSharedApplication().preferredContentSizeCategory;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ extern NSString *const RCTAccessibilityManagerDidUpdateMultiplierNotification; /
@property (nonatomic, assign) BOOL isGrayscaleEnabled;
@property (nonatomic, assign) BOOL isInvertColorsEnabled;
@property (nonatomic, assign) BOOL isReduceMotionEnabled;
@property (nonatomic, assign) BOOL isDarkerSystemColorsEnabled;
@property (nonatomic, assign) BOOL isHighContrastEnabled;
@property (nonatomic, assign) BOOL prefersCrossFadeTransitions;
@property (nonatomic, assign) BOOL isReduceTransparencyEnabled;
@property (nonatomic, assign) BOOL isVoiceOverEnabled;
Expand Down
27 changes: 18 additions & 9 deletions packages/react-native/React/CoreModules/RCTAccessibilityManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,9 @@ - (instancetype)init
object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(darkerSystemColorsDidChange:)
selector:@selector(highContrastDidChange:)
name:UIAccessibilityDarkerSystemColorsStatusDidChangeNotification
object:nil];

[[NSNotificationCenter defaultCenter] addObserver:self
selector:@selector(reduceTransparencyStatusDidChange:)
name:UIAccessibilityReduceTransparencyStatusDidChangeNotification
Expand All @@ -98,7 +97,7 @@ - (instancetype)init
_isGrayscaleEnabled = initialValuesProxy.isGrayscaleEnabled;
_isInvertColorsEnabled = initialValuesProxy.isInvertColorsEnabled;
_isReduceMotionEnabled = initialValuesProxy.isReduceMotionEnabled;
_isDarkerSystemColorsEnabled = initialValuesProxy.isDarkerSystemColorsEnabled;
_isHighContrastEnabled = initialValuesProxy.isHighContrastEnabled;
_isReduceTransparencyEnabled = initialValuesProxy.isReduceTransparencyEnabled;
_isVoiceOverEnabled = initialValuesProxy.isVoiceOverEnabled;
}
Expand Down Expand Up @@ -177,15 +176,18 @@ - (void)reduceMotionStatusDidChange:(__unused NSNotification *)notification
}
}

- (void)darkerSystemColorsDidChange:(__unused NSNotification *)notification
- (void)highContrastDidChange:(__unused NSNotification *)notification
{
BOOL newDarkerSystemColorsEnabled = UIAccessibilityDarkerSystemColorsEnabled();
if (_isDarkerSystemColorsEnabled != newDarkerSystemColorsEnabled) {
_isDarkerSystemColorsEnabled = newDarkerSystemColorsEnabled;
BOOL newHighContrastEnabled = UIAccessibilityDarkerSystemColorsEnabled();
if (_isHighContrastEnabled != newHighContrastEnabled) {
_isHighContrastEnabled = newHighContrastEnabled;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// TODO: Remove once `isDarkerSystemColorsEnabled` is removed from AccessibilityInfo.js
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"darkerSystemColorsChanged"
body:@(_isDarkerSystemColorsEnabled)];
body:@(_isHighContrastEnabled)];
[[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"highContrastChanged"
body:@(_isHighContrastEnabled)];

#pragma clang diagnostic pop
}
Expand Down Expand Up @@ -380,7 +382,14 @@ static void setMultipliers(
: (RCTResponseSenderBlock)onSuccess onError
: (__unused RCTResponseSenderBlock)onError)
{
onSuccess(@[ @(_isDarkerSystemColorsEnabled) ]);
onSuccess(@[ @(_isHighContrastEnabled) ]);
}

RCT_EXPORT_METHOD(getCurrentHighContrastState
: (RCTResponseSenderBlock)onSuccess onError
: (__unused RCTResponseSenderBlock)onError)
{
onSuccess(@[ @(_isHighContrastEnabled) ]);
}

RCT_EXPORT_METHOD(getCurrentPrefersCrossFadeTransitionsState
Expand Down
Loading
Loading