diff --git a/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts b/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts index 6914853d2500f9..fea55884f5709c 100644 --- a/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts +++ b/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.d.ts @@ -17,6 +17,7 @@ type AccessibilityChangeEventName = | 'invertColorsChanged' // iOS-only Event | 'reduceMotionChanged' | 'highTextContrastChanged' // Android-only Event + | 'darkerSystemColorsChanged' // iOS-only Event | 'screenReaderChanged' | 'reduceTransparencyChanged'; // iOS-only Event @@ -77,6 +78,13 @@ export interface AccessibilityInfoStatic { */ isHighTextContrastEnabled: () => Promise; + /** + * Query whether darker system colors is currently enabled. + * + * @platform ios + */ + isDarkerSystemColorsEnabled: () => Promise; + /** * Query whether reduce motion and prefer cross-fade transitions settings are currently enabled. * diff --git a/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js b/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js index 755d0f3ed678e4..73940778356f57 100644 --- a/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js +++ b/packages/react-native/Libraries/Components/AccessibilityInfo/AccessibilityInfo.js @@ -31,6 +31,7 @@ type AccessibilityEventDefinitionsIOS = { grayscaleChanged: [boolean], invertColorsChanged: [boolean], reduceTransparencyChanged: [boolean], + darkerSystemColorsChanged: [boolean], }; type AccessibilityEventDefinitions = { @@ -64,6 +65,7 @@ const EventNames: Map< ['reduceMotionChanged', 'reduceMotionChanged'], ['reduceTransparencyChanged', 'reduceTransparencyChanged'], ['screenReaderChanged', 'screenReaderChanged'], + ['darkerSystemColorsChanged', 'darkerSystemColorsChanged'], ]); /** @@ -200,6 +202,32 @@ const AccessibilityInfo = { }); }, + /** + * Query whether dark system colors is currently enabled. iOS only. + * + * Returns a promise which resolves to a boolean. + * The result is `true` when dark system colors is enabled and `false` otherwise. + */ + isDarkerSystemColorsEnabled(): Promise { + return new Promise((resolve, reject) => { + if (Platform.OS === 'android') { + return Promise.resolve(false); + } else { + if ( + NativeAccessibilityManagerIOS?.getCurrentDarkerSystemColorsState != + null + ) { + NativeAccessibilityManagerIOS.getCurrentDarkerSystemColorsState( + resolve, + reject, + ); + } else { + reject(null); + } + } + }); + }, + /** * Query whether reduce motion and prefer cross-fade transitions settings are currently enabled. * @@ -340,6 +368,9 @@ const AccessibilityInfo = { * - `announcement`: The string announced by the screen reader. * - `success`: A boolean indicating whether the announcement was * successfully made. + * - `darkerSystemColorsChanged`: iOS-only event. Fires when the state of the dark system colors + * toggle changes. The argument to the event handler is a boolean. The boolean is `true` when + * dark system colors is enabled and `false` otherwise. * * These events are only supported on Android: * diff --git a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap index 2bceffd5851558..15cd6c00cce1ac 100644 --- a/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap +++ b/packages/react-native/Libraries/__tests__/__snapshots__/public-api-test.js.snap @@ -1583,6 +1583,7 @@ type AccessibilityEventDefinitionsIOS = { grayscaleChanged: [boolean], invertColorsChanged: [boolean], reduceTransparencyChanged: [boolean], + darkerSystemColorsChanged: [boolean], }; type AccessibilityEventDefinitions = { ...AccessibilityEventDefinitionsAndroid, @@ -1598,6 +1599,7 @@ declare const AccessibilityInfo: { isInvertColorsEnabled(): Promise, isReduceMotionEnabled(): Promise, isHighTextContrastEnabled(): Promise, + isDarkerSystemColorsEnabled(): Promise, prefersCrossFadeTransitions(): Promise, isReduceTransparencyEnabled(): Promise, isScreenReaderEnabled(): Promise, diff --git a/packages/react-native/React/CoreModules/RCTAccessibilityManager.h b/packages/react-native/React/CoreModules/RCTAccessibilityManager.h index 01af72e81420ff..e8ea831b931b0d 100644 --- a/packages/react-native/React/CoreModules/RCTAccessibilityManager.h +++ b/packages/react-native/React/CoreModules/RCTAccessibilityManager.h @@ -24,6 +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 prefersCrossFadeTransitions; @property (nonatomic, assign) BOOL isReduceTransparencyEnabled; @property (nonatomic, assign) BOOL isVoiceOverEnabled; diff --git a/packages/react-native/React/CoreModules/RCTAccessibilityManager.mm b/packages/react-native/React/CoreModules/RCTAccessibilityManager.mm index 4aaabcaba30be7..c32a12d57a776f 100644 --- a/packages/react-native/React/CoreModules/RCTAccessibilityManager.mm +++ b/packages/react-native/React/CoreModules/RCTAccessibilityManager.mm @@ -76,6 +76,11 @@ - (instancetype)init name:UIAccessibilityReduceMotionStatusDidChangeNotification object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(darkerSystemColorsDidChange:) + name:UIAccessibilityDarkerSystemColorsStatusDidChangeNotification + object:nil]; + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(reduceTransparencyStatusDidChange:) name:UIAccessibilityReduceTransparencyStatusDidChangeNotification @@ -91,6 +96,7 @@ - (instancetype)init _isGrayscaleEnabled = UIAccessibilityIsGrayscaleEnabled(); _isInvertColorsEnabled = UIAccessibilityIsInvertColorsEnabled(); _isReduceMotionEnabled = UIAccessibilityIsReduceMotionEnabled(); + _isDarkerSystemColorsEnabled = UIAccessibilityDarkerSystemColorsEnabled(); _isReduceTransparencyEnabled = UIAccessibilityIsReduceTransparencyEnabled(); _isVoiceOverEnabled = UIAccessibilityIsVoiceOverRunning(); } @@ -169,6 +175,20 @@ - (void)reduceMotionStatusDidChange:(__unused NSNotification *)notification } } +- (void)darkerSystemColorsDidChange:(__unused NSNotification *)notification +{ + BOOL newDarkerSystemColorsEnabled = UIAccessibilityDarkerSystemColorsEnabled(); + if (_isDarkerSystemColorsEnabled != newDarkerSystemColorsEnabled) { + _isDarkerSystemColorsEnabled = newDarkerSystemColorsEnabled; +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + [[_moduleRegistry moduleForName:"EventDispatcher"] sendDeviceEventWithName:@"darkerSystemColorsChanged" + body:@(_isDarkerSystemColorsEnabled)]; + +#pragma clang diagnostic pop + } +} + - (void)reduceTransparencyStatusDidChange:(__unused NSNotification *)notification { BOOL newReduceTransparencyEnabled = UIAccessibilityIsReduceTransparencyEnabled(); @@ -354,6 +374,13 @@ static void setMultipliers( onSuccess(@[ @(_isReduceMotionEnabled) ]); } +RCT_EXPORT_METHOD(getCurrentDarkerSystemColorsState + : (RCTResponseSenderBlock)onSuccess onError + : (__unused RCTResponseSenderBlock)onError) +{ + onSuccess(@[ @(_isDarkerSystemColorsEnabled) ]); +} + RCT_EXPORT_METHOD(getCurrentPrefersCrossFadeTransitionsState : (RCTResponseSenderBlock)onSuccess onError : (__unused RCTResponseSenderBlock)onError) diff --git a/packages/react-native/jest/setup.js b/packages/react-native/jest/setup.js index c7f165b264173e..667100ed83e1f2 100644 --- a/packages/react-native/jest/setup.js +++ b/packages/react-native/jest/setup.js @@ -152,6 +152,7 @@ jest isInvertColorsEnabled: jest.fn(() => Promise.resolve(false)), isReduceMotionEnabled: jest.fn(() => Promise.resolve(false)), isHighTextContrastEnabled: jest.fn(() => Promise.resolve(false)), + isDarkerSystemColorsEnabled: jest.fn(() => Promise.resolve(false)), prefersCrossFadeTransitions: jest.fn(() => Promise.resolve(false)), isReduceTransparencyEnabled: jest.fn(() => Promise.resolve(false)), isScreenReaderEnabled: jest.fn(() => Promise.resolve(false)), diff --git a/packages/react-native/src/private/specs/modules/NativeAccessibilityManager.js b/packages/react-native/src/private/specs/modules/NativeAccessibilityManager.js index 20db1734da5866..6a978c3ea18560 100644 --- a/packages/react-native/src/private/specs/modules/NativeAccessibilityManager.js +++ b/packages/react-native/src/private/specs/modules/NativeAccessibilityManager.js @@ -29,6 +29,10 @@ export interface Spec extends TurboModule { onSuccess: (isReduceMotionEnabled: boolean) => void, onError: (error: Object) => void, ) => void; + +getCurrentDarkerSystemColorsState?: ( + onSuccess: (isDarkerSystemColorsEnabled: boolean) => void, + onError: (error: Object) => void, + ) => void; +getCurrentPrefersCrossFadeTransitionsState?: ( onSuccess: (prefersCrossFadeTransitions: boolean) => void, onError: (error: Object) => void,