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

Commit 6ec595d

Browse files
committed
added a test that exercises announcing route changes
1 parent f3a38f0 commit 6ec595d

File tree

3 files changed

+96
-7
lines changed

3 files changed

+96
-7
lines changed

shell/platform/darwin/ios/framework/Source/accessibility_bridge.h

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,18 @@ class PlatformViewIOS;
3636
*/
3737
class AccessibilityBridge final : public AccessibilityBridgeIos {
3838
public:
39+
/** Delegate for handling iOS operations. */
40+
class IosDelegate {
41+
public:
42+
virtual ~IosDelegate() = default;
43+
virtual void PostAccessibilityNotification(UIAccessibilityNotifications notification,
44+
id argument) = 0;
45+
};
46+
3947
AccessibilityBridge(UIView* view,
4048
PlatformViewIOS* platform_view,
41-
FlutterPlatformViewsController* platform_views_controller);
49+
FlutterPlatformViewsController* platform_views_controller,
50+
std::unique_ptr<IosDelegate> ios_delegate = nullptr);
4251
~AccessibilityBridge();
4352

4453
void UpdateSemantics(flutter::SemanticsNodeUpdates nodes,
@@ -75,6 +84,7 @@ class AccessibilityBridge final : public AccessibilityBridgeIos {
7584
int32_t previous_route_id_;
7685
std::unordered_map<int32_t, flutter::CustomAccessibilityAction> actions_;
7786
std::vector<int32_t> previous_routes_;
87+
std::unique_ptr<IosDelegate> ios_delegate_;
7888

7989
FML_DISALLOW_COPY_AND_ASSIGN(AccessibilityBridge);
8090
};

shell/platform/darwin/ios/framework/Source/accessibility_bridge.mm

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,29 @@
1313
FLUTTER_ASSERT_NOT_ARC
1414

1515
namespace flutter {
16+
namespace {
17+
class DefaultIosDelegate : public AccessibilityBridge::IosDelegate {
18+
public:
19+
void PostAccessibilityNotification(UIAccessibilityNotifications notification,
20+
id argument) override {
21+
UIAccessibilityPostNotification(notification, argument);
22+
}
23+
};
24+
} // namespace
1625

1726
AccessibilityBridge::AccessibilityBridge(UIView* view,
1827
PlatformViewIOS* platform_view,
19-
FlutterPlatformViewsController* platform_views_controller)
28+
FlutterPlatformViewsController* platform_views_controller,
29+
std::unique_ptr<IosDelegate> ios_delegate)
2030
: view_(view),
2131
platform_view_(platform_view),
2232
platform_views_controller_(platform_views_controller),
2333
objects_([[NSMutableDictionary alloc] init]),
2434
weak_factory_(this),
2535
previous_route_id_(0),
26-
previous_routes_({}) {
36+
previous_routes_({}),
37+
ios_delegate_(ios_delegate ? std::move(ios_delegate)
38+
: std::make_unique<DefaultIosDelegate>()) {
2739
accessibility_channel_.reset([[FlutterBasicMessageChannel alloc]
2840
initWithName:@"flutter/accessibility"
2941
binaryMessenger:platform_view->GetOwnerViewController().get().engine.binaryMessenger
@@ -138,14 +150,15 @@
138150
layoutChanged = layoutChanged || [doomed_uids count] > 0;
139151
if (routeChanged) {
140152
NSString* routeName = [lastAdded routeName];
141-
UIAccessibilityPostNotification(UIAccessibilityScreenChangedNotification, routeName);
153+
ios_delegate_->PostAccessibilityNotification(UIAccessibilityScreenChangedNotification,
154+
routeName);
142155
} else if (layoutChanged) {
143156
// TODO(goderbauer): figure out which node to focus next.
144-
UIAccessibilityPostNotification(UIAccessibilityLayoutChangedNotification, nil);
157+
ios_delegate_->PostAccessibilityNotification(UIAccessibilityLayoutChangedNotification, nil);
145158
}
146159
if (scrollOccured) {
147160
// TODO(tvolkert): provide meaningful string (e.g. "page 2 of 5")
148-
UIAccessibilityPostNotification(UIAccessibilityPageScrolledNotification, @"");
161+
ios_delegate_->PostAccessibilityNotification(UIAccessibilityPageScrolledNotification, @"");
149162
}
150163
}
151164

@@ -233,7 +246,7 @@ static bool DidFlagChange(const flutter::SemanticsNode& oldNode,
233246
NSString* type = annotatedEvent[@"type"];
234247
if ([type isEqualToString:@"announce"]) {
235248
NSString* message = annotatedEvent[@"data"][@"message"];
236-
UIAccessibilityPostNotification(UIAccessibilityAnnouncementNotification, message);
249+
ios_delegate_->PostAccessibilityNotification(UIAccessibilityAnnouncementNotification, message);
237250
}
238251
}
239252

shell/platform/darwin/ios/framework/Source/accessibility_bridge_test.mm

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,17 @@ void OnPlatformViewRegisterTexture(std::shared_ptr<Texture> texture) override {}
8686
void OnPlatformViewUnregisterTexture(int64_t texture_id) override {}
8787
void OnPlatformViewMarkTextureFrameAvailable(int64_t texture_id) override {}
8888
};
89+
90+
class MockIosDelegate : public AccessibilityBridge::IosDelegate {
91+
public:
92+
void PostAccessibilityNotification(UIAccessibilityNotifications notification,
93+
id argument) override {
94+
if (on_PostAccessibilityNotification_) {
95+
on_PostAccessibilityNotification_(notification, argument);
96+
}
97+
}
98+
std::function<void(UIAccessibilityNotifications, id)> on_PostAccessibilityNotification_;
99+
};
89100
} // namespace
90101
} // namespace flutter
91102

@@ -238,4 +249,59 @@ - (void)testSemanticsDeallocated {
238249
XCTAssertNil(gMockPlatformView);
239250
}
240251

252+
- (void)testAnnouncesRouteChanges {
253+
flutter::MockDelegate mock_delegate;
254+
auto thread_task_runner = CreateNewThread("AccessibilityBridgeTest");
255+
flutter::TaskRunners runners(/*label=*/self.name.UTF8String,
256+
/*platform=*/thread_task_runner,
257+
/*raster=*/thread_task_runner,
258+
/*ui=*/thread_task_runner,
259+
/*io=*/thread_task_runner);
260+
auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
261+
/*delegate=*/mock_delegate,
262+
/*rendering_api=*/flutter::IOSRenderingAPI::kSoftware,
263+
/*task_runners=*/runners);
264+
id mockFlutterView = OCMClassMock([FlutterView class]);
265+
std::string label = "some label";
266+
267+
NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
268+
[[[NSMutableArray alloc] init] autorelease];
269+
auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
270+
ios_delegate->on_PostAccessibilityNotification_ =
271+
[accessibility_notifications](UIAccessibilityNotifications notification, id argument) {
272+
[accessibility_notifications addObject:@{
273+
@"notification" : @(notification),
274+
@"argument" : argument ? argument : [NSNull null],
275+
}];
276+
};
277+
__block auto bridge =
278+
std::make_unique<flutter::AccessibilityBridge>(/*view=*/mockFlutterView,
279+
/*platform_view=*/platform_view.get(),
280+
/*platform_views_controller=*/nil,
281+
/*ios_delegate=*/std::move(ios_delegate));
282+
283+
flutter::CustomAccessibilityActionUpdates actions;
284+
flutter::SemanticsNodeUpdates nodes;
285+
286+
flutter::SemanticsNode route_node;
287+
route_node.id = 1;
288+
route_node.label = label;
289+
route_node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kScopesRoute) |
290+
static_cast<int32_t>(flutter::SemanticsFlags::kNamesRoute);
291+
route_node.label = "route";
292+
nodes[route_node.id] = route_node;
293+
flutter::SemanticsNode root_node;
294+
root_node.id = kRootNodeId;
295+
root_node.label = label;
296+
root_node.childrenInTraversalOrder = {1};
297+
root_node.childrenInHitTestOrder = {1};
298+
nodes[root_node.id] = root_node;
299+
bridge->UpdateSemantics(/*nodes=*/nodes, /*actions=*/actions);
300+
301+
XCTAssertEqual([accessibility_notifications count], 1ul);
302+
XCTAssertEqualObjects(accessibility_notifications[0][@"argument"], @"route");
303+
XCTAssertEqual([accessibility_notifications[0][@"notification"] unsignedIntValue],
304+
UIAccessibilityScreenChangedNotification);
305+
}
306+
241307
@end

0 commit comments

Comments
 (0)