Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 89c1b23

Browse files
authored
[macOS]Support SemanticsService.announce (#40585)
[macOS]Support SemanticsService.announce
1 parent 047fc60 commit 89c1b23

File tree

3 files changed

+76
-0
lines changed

3 files changed

+76
-0
lines changed

shell/platform/darwin/macos/framework/Source/FlutterEngine.mm

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,11 @@ - (void)loadAOTData:(NSString*)assetsDir;
150150
*/
151151
- (void)setUpPlatformViewChannel;
152152

153+
/**
154+
* Creates an accessibility channel and sets up the message handler.
155+
*/
156+
- (void)setUpAccessibilityChannel;
157+
153158
/**
154159
* Handles messages received from the Flutter engine on the _*Channel channels.
155160
*/
@@ -366,6 +371,9 @@ @implementation FlutterEngine {
366371
// A message channel for sending user settings to the flutter engine.
367372
FlutterBasicMessageChannel* _settingsChannel;
368373

374+
// A message channel for accessibility.
375+
FlutterBasicMessageChannel* _accessibilityChannel;
376+
369377
// A method channel for miscellaneous platform functionality.
370378
FlutterMethodChannel* _platformChannel;
371379

@@ -409,6 +417,7 @@ - (instancetype)initWithName:(NSString*)labelPrefix
409417

410418
_platformViewController = [[FlutterPlatformViewController alloc] init];
411419
[self setUpPlatformViewChannel];
420+
[self setUpAccessibilityChannel];
412421
[self setUpNotificationCenterListeners];
413422

414423
return self;
@@ -923,6 +932,16 @@ - (void)setUpPlatformViewChannel {
923932
}];
924933
}
925934

935+
- (void)setUpAccessibilityChannel {
936+
_accessibilityChannel = [FlutterBasicMessageChannel
937+
messageChannelWithName:@"flutter/accessibility"
938+
binaryMessenger:self.binaryMessenger
939+
codec:[FlutterStandardMessageCodec sharedInstance]];
940+
__weak FlutterEngine* weakSelf = self;
941+
[_accessibilityChannel setMessageHandler:^(id message, FlutterReply reply) {
942+
[weakSelf handleAccessibilityEvent:message];
943+
}];
944+
}
926945
- (void)setUpNotificationCenterListeners {
927946
NSNotificationCenter* center = [NSNotificationCenter defaultCenter];
928947
// macOS fires this private message when VoiceOver turns on or off.
@@ -967,7 +986,30 @@ - (void)onAccessibilityStatusChanged:(NSNotification*)notification {
967986

968987
self.semanticsEnabled = enabled;
969988
}
989+
- (void)handleAccessibilityEvent:(NSDictionary<NSString*, id>*)annotatedEvent {
990+
NSString* type = annotatedEvent[@"type"];
991+
if ([type isEqualToString:@"announce"]) {
992+
NSString* message = annotatedEvent[@"data"][@"message"];
993+
NSNumber* assertiveness = annotatedEvent[@"data"][@"assertiveness"];
994+
if (message == nil) {
995+
return;
996+
}
970997

998+
NSAccessibilityPriorityLevel priority = [assertiveness isEqualToNumber:@1]
999+
? NSAccessibilityPriorityHigh
1000+
: NSAccessibilityPriorityMedium;
1001+
1002+
[self announceAccessibilityMessage:message withPriority:priority];
1003+
}
1004+
}
1005+
1006+
- (void)announceAccessibilityMessage:(NSString*)message
1007+
withPriority:(NSAccessibilityPriorityLevel)priority {
1008+
NSAccessibilityPostNotificationWithUserInfo(
1009+
[self viewControllerForId:kFlutterDefaultViewId].flutterView,
1010+
NSAccessibilityAnnouncementRequestedNotification,
1011+
@{NSAccessibilityAnnouncementKey : message, NSAccessibilityPriorityKey : @(priority)});
1012+
}
9711013
- (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result {
9721014
if ([call.method isEqualToString:@"SystemNavigator.pop"]) {
9731015
[NSApp terminate:self];

shell/platform/darwin/macos/framework/Source/FlutterEngineTest.mm

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -766,6 +766,29 @@ - (nonnull NSView*)createWithViewIdentifier:(int64_t)viewId arguments:(nullable
766766
EXPECT_TRUE(triedToTerminate);
767767
}
768768

769+
TEST_F(FlutterEngineTest, HandleAccessibilityEvent) {
770+
__block BOOL announced = FALSE;
771+
id engineMock = CreateMockFlutterEngine(nil);
772+
773+
OCMStub([engineMock announceAccessibilityMessage:[OCMArg any]
774+
withPriority:NSAccessibilityPriorityMedium])
775+
.andDo((^(NSInvocation* invocation) {
776+
announced = TRUE;
777+
[invocation retainArguments];
778+
NSString* message;
779+
[invocation getArgument:&message atIndex:2];
780+
EXPECT_EQ(message, @"error message");
781+
}));
782+
783+
NSDictionary<NSString*, id>* annotatedEvent =
784+
@{@"type" : @"announce",
785+
@"data" : @{@"message" : @"error message"}};
786+
787+
[engineMock handleAccessibilityEvent:annotatedEvent];
788+
789+
EXPECT_TRUE(announced);
790+
}
791+
769792
} // namespace flutter::testing
770793

771794
// NOLINTEND(clang-analyzer-core.StackAddressEscape)

shell/platform/darwin/macos/framework/Source/FlutterEngine_Internal.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,17 @@ typedef NS_ENUM(NSInteger, FlutterAppExitResponse) {
179179
toTarget:(uint16_t)target
180180
withData:(fml::MallocMapping)data;
181181

182+
/**
183+
* Handles accessibility events.
184+
*/
185+
- (void)handleAccessibilityEvent:(NSDictionary<NSString*, id>*)annotatedEvent;
186+
187+
/**
188+
* Announces accessibility messages.
189+
*/
190+
- (void)announceAccessibilityMessage:(NSString*)message
191+
withPriority:(NSAccessibilityPriorityLevel)priority;
192+
182193
@end
183194

184195
NS_ASSUME_NONNULL_END

0 commit comments

Comments
 (0)