diff --git a/lib/ui/window.dart b/lib/ui/window.dart index 7fd11a9be7011..54d3c606ce301 100644 --- a/lib/ui/window.dart +++ b/lib/ui/window.dart @@ -771,6 +771,7 @@ class AccessibilityFeatures { static const int _kBoldTextIndex = 1 << 3; static const int _kReduceMotionIndex = 1 << 4; static const int _kHighContrastIndex = 1 << 5; + static const int _kOnOffSwitchLabelsIndex = 1 << 6; // A bitfield which represents each enabled feature. final int _index; @@ -803,6 +804,11 @@ class AccessibilityFeatures { /// Only supported on iOS. bool get highContrast => _kHighContrastIndex & _index != 0; + /// The platform is requesting to show on/off labels inside switches. + /// + /// Only supported on iOS. + bool get onOffSwitchLabels => _kOnOffSwitchLabelsIndex & _index != 0; + @override String toString() { final List features = []; @@ -818,6 +824,8 @@ class AccessibilityFeatures { features.add('reduceMotion'); if (highContrast) features.add('highContrast'); + if (onOffSwitchLabels) + features.add('onOffSwitchLabels'); return 'AccessibilityFeatures$features'; } diff --git a/lib/ui/window/platform_configuration.h b/lib/ui/window/platform_configuration.h index d42f8639a9bcd..d112b2dcc002e 100644 --- a/lib/ui/window/platform_configuration.h +++ b/lib/ui/window/platform_configuration.h @@ -35,6 +35,7 @@ enum class AccessibilityFeatureFlag : int32_t { kBoldText = 1 << 3, kReduceMotion = 1 << 4, kHighContrast = 1 << 5, + kOnOffSwitchLabels = 1 << 6, }; //-------------------------------------------------------------------------- diff --git a/lib/web_ui/lib/window.dart b/lib/web_ui/lib/window.dart index b60ab1180794c..a5dc10f17f0b4 100644 --- a/lib/web_ui/lib/window.dart +++ b/lib/web_ui/lib/window.dart @@ -148,6 +148,7 @@ class AccessibilityFeatures { static const int _kBoldTextIndex = 1 << 3; static const int _kReduceMotionIndex = 1 << 4; static const int _kHighContrastIndex = 1 << 5; + static const int _kOnOffSwitchLabelsIndex = 1 << 6; // A bitfield which represents each enabled feature. final int _index; @@ -158,6 +159,7 @@ class AccessibilityFeatures { bool get boldText => _kBoldTextIndex & _index != 0; bool get reduceMotion => _kReduceMotionIndex & _index != 0; bool get highContrast => _kHighContrastIndex & _index != 0; + bool get onOffSwitchLabels => _kOnOffSwitchLabelsIndex & _index != 0; @override String toString() { @@ -180,6 +182,9 @@ class AccessibilityFeatures { if (highContrast) { features.add('highContrast'); } + if (onOffSwitchLabels) { + features.add('onOffSwitchLabels'); + } return 'AccessibilityFeatures$features'; } diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm index 08f44786fb04e..c53a8778d7995 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController.mm @@ -331,6 +331,13 @@ - (void)setupNotificationCenterObservers { name:UIAccessibilityDarkerSystemColorsStatusDidChangeNotification object:nil]; + if (@available(iOS 13.0, *)) { + [center addObserver:self + selector:@selector(onAccessibilityStatusChanged:) + name:UIAccessibilityOnOffSwitchLabelsDidChangeNotification + object:nil]; + } + [center addObserver:self selector:@selector(onUserSettingsChanged:) name:UIContentSizeCategoryDidChangeNotification @@ -1417,19 +1424,7 @@ - (void)onAccessibilityStatusChanged:(NSNotification*)notification { return; } auto platformView = [_engine.get() platformView]; - int32_t flags = 0; - if (UIAccessibilityIsInvertColorsEnabled()) { - flags |= static_cast(flutter::AccessibilityFeatureFlag::kInvertColors); - } - if (UIAccessibilityIsReduceMotionEnabled()) { - flags |= static_cast(flutter::AccessibilityFeatureFlag::kReduceMotion); - } - if (UIAccessibilityIsBoldTextEnabled()) { - flags |= static_cast(flutter::AccessibilityFeatureFlag::kBoldText); - } - if (UIAccessibilityDarkerSystemColorsEnabled()) { - flags |= static_cast(flutter::AccessibilityFeatureFlag::kHighContrast); - } + int32_t flags = [self accessibilityFlags]; #if TARGET_OS_SIMULATOR // There doesn't appear to be any way to determine whether the accessibility // inspector is enabled on the simulator. We conservatively always turn on the @@ -1447,6 +1442,35 @@ - (void)onAccessibilityStatusChanged:(NSNotification*)notification { #endif } +- (int32_t)accessibilityFlags { + int32_t flags = 0; + if (UIAccessibilityIsInvertColorsEnabled()) { + flags |= static_cast(flutter::AccessibilityFeatureFlag::kInvertColors); + } + if (UIAccessibilityIsReduceMotionEnabled()) { + flags |= static_cast(flutter::AccessibilityFeatureFlag::kReduceMotion); + } + if (UIAccessibilityIsBoldTextEnabled()) { + flags |= static_cast(flutter::AccessibilityFeatureFlag::kBoldText); + } + if (UIAccessibilityDarkerSystemColorsEnabled()) { + flags |= static_cast(flutter::AccessibilityFeatureFlag::kHighContrast); + } + if ([FlutterViewController accessibilityIsOnOffSwitchLabelsEnabled]) { + flags |= static_cast(flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels); + } + + return flags; +} + ++ (BOOL)accessibilityIsOnOffSwitchLabelsEnabled { + if (@available(iOS 13, *)) { + return UIAccessibilityIsOnOffSwitchLabelsEnabled(); + } else { + return NO; + } +} + #pragma mark - Set user settings - (void)traitCollectionDidChange:(UITraitCollection*)previousTraitCollection { diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm index a91101c36807b..d6f3e0f9a1f78 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewControllerTest.mm @@ -6,6 +6,7 @@ #import #include "flutter/fml/platform/darwin/message_loop_darwin.h" +#import "flutter/lib/ui/window/platform_configuration.h" #import "flutter/lib/ui/window/viewport_metrics.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterBinaryMessenger.h" #import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h" @@ -25,10 +26,6 @@ - (void)sendKeyEvent:(const FlutterKeyEvent&)event userData:(nullable void*)userData; @end -namespace flutter { -class PointerDataPacket {}; -} - /// Sometimes we have to use a custom mock to avoid retain cycles in OCMock. /// Used for testing low memory notification. @interface FlutterEnginePartialMock : FlutterEngine @@ -617,6 +614,46 @@ - (void)testItReportsHighContrastWhenTraitCollectionRequestsIt { [mockTraitCollection stopMocking]; } +- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagNotSet { + if (@available(iOS 13, *)) { + // noop + } else { + return; + } + + // Setup test. + FlutterViewController* viewController = + [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; + id partialMockViewController = OCMPartialMock(viewController); + OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(NO); + + // Exercise behavior under test. + int32_t flags = [partialMockViewController accessibilityFlags]; + + // Verify behavior. + XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) == 0); +} + +- (void)testItReportsAccessibilityOnOffSwitchLabelsFlagSet { + if (@available(iOS 13, *)) { + // noop + } else { + return; + } + + // Setup test. + FlutterViewController* viewController = + [[FlutterViewController alloc] initWithEngine:self.mockEngine nibName:nil bundle:nil]; + id partialMockViewController = OCMPartialMock(viewController); + OCMStub([partialMockViewController accessibilityIsOnOffSwitchLabelsEnabled]).andReturn(YES); + + // Exercise behavior under test. + int32_t flags = [partialMockViewController accessibilityFlags]; + + // Verify behavior. + XCTAssert((flags & (int32_t)flutter::AccessibilityFeatureFlag::kOnOffSwitchLabels) != 0); +} + - (void)testPerformOrientationUpdateForcesOrientationChange { [self orientationTestWithOrientationUpdate:UIInterfaceOrientationMaskPortrait currentOrientation:UIInterfaceOrientationLandscapeLeft @@ -1052,7 +1089,7 @@ - (void)testMouseSupport API_AVAILABLE(ios(13.4)) { [vc scrollEvent:mockPanGestureRecognizer]; [[[self.mockEngine verify] ignoringNonObjectArgs] - dispatchPointerDataPacket:std::make_unique()]; + dispatchPointerDataPacket:std::make_unique(0)]; } @end diff --git a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h index 9e069b467828a..1b4af46a68b36 100644 --- a/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h +++ b/shell/platform/darwin/ios/framework/Source/FlutterViewController_Internal.h @@ -29,6 +29,7 @@ extern NSNotificationName const FlutterViewControllerShowHomeIndicator; @interface FlutterViewController () +@property(class, nonatomic, readonly) BOOL accessibilityIsOnOffSwitchLabelsEnabled; @property(nonatomic, readonly) BOOL isPresentingViewController; @property(nonatomic, readonly) BOOL isVoiceOverRunning; @property(nonatomic, retain) FlutterKeyboardManager* keyboardManager; @@ -45,6 +46,7 @@ extern NSNotificationName const FlutterViewControllerShowHomeIndicator; nextAction:(void (^)())nextAction API_AVAILABLE(ios(13.4)); - (void)addInternalPlugins; - (void)deregisterNotifications; +- (int32_t)accessibilityFlags; @end #endif // FLUTTER_SHELL_PLATFORM_DARWIN_IOS_FRAMEWORK_SOURCE_FLUTTERVIEWCONTROLLER_INTERNAL_H_