Skip to content

Commit

Permalink
Sets accessibility panel title when route changes (flutter#27713)
Browse files Browse the repository at this point in the history
  • Loading branch information
chunhtai authored Jul 29, 2021
1 parent 7996f2f commit c889b02
Show file tree
Hide file tree
Showing 2 changed files with 32 additions and 60 deletions.
30 changes: 22 additions & 8 deletions shell/platform/android/io/flutter/view/AccessibilityBridge.java
Original file line number Diff line number Diff line change
Expand Up @@ -1566,7 +1566,7 @@ void updateSemantics(
if (lastAdded != null
&& (lastAdded.id != previousRouteId || newRoutes.size() != flutterNavigationStack.size())) {
previousRouteId = lastAdded.id;
sendWindowChangeEvent(lastAdded);
onWindowNameChange(lastAdded);
}
flutterNavigationStack.clear();
for (SemanticsNode semanticsNode : newRoutes) {
Expand Down Expand Up @@ -1778,15 +1778,17 @@ private void sendAccessibilityEvent(@NonNull AccessibilityEvent event) {
}

/**
* Creates a {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} and sends the event to Android's
* accessibility system.
* Informs the TalkBack user about window name changes.
*
* <p>This method sets accessibility panel title if the API level >= 28, otherwise, it creates a
* {@link AccessibilityEvent#TYPE_WINDOW_STATE_CHANGED} and sends the event to Android's
* accessibility system. In both cases, TalkBack announces the label of the route and re-addjusts
* the accessibility focus.
*
* <p>The given {@code route} should be a {@link SemanticsNode} that represents a navigation route
* in the Flutter app.
*/
private void sendWindowChangeEvent(@NonNull SemanticsNode route) {
AccessibilityEvent event =
obtainAccessibilityEvent(route.id, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
private void onWindowNameChange(@NonNull SemanticsNode route) {
String routeName = route.getRouteName();
if (routeName == null) {
// The routeName will be null when there is no semantics node that represnets namesRoute in
Expand All @@ -1799,8 +1801,20 @@ private void sendWindowChangeEvent(@NonNull SemanticsNode route) {
// next.
routeName = " ";
}
event.getText().add(routeName);
sendAccessibilityEvent(event);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
setAccessibilityPaneTitle(routeName);
} else {
AccessibilityEvent event =
obtainAccessibilityEvent(route.id, AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
event.getText().add(routeName);
sendAccessibilityEvent(event);
}
}

@TargetApi(28)
@RequiresApi(28)
private void setAccessibilityPaneTitle(String title) {
rootAccessibilityView.setAccessibilityPaneTitle(title);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,15 +210,7 @@ public void itAnnouncesRouteNameWhenAddingNewRoute() {
TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);

ArgumentCaptor<AccessibilityEvent> eventCaptor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mockParent, times(2))
.requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
AccessibilityEvent event = eventCaptor.getAllValues().get(0);
assertEquals(event.getEventType(), AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
List<CharSequence> sentences = event.getText();
assertEquals(sentences.size(), 1);
assertEquals(sentences.get(0).toString(), "node1");
verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("node1"));

TestSemanticsNode new_root = new TestSemanticsNode();
new_root.id = 0;
Expand All @@ -237,14 +229,7 @@ public void itAnnouncesRouteNameWhenAddingNewRoute() {
testSemanticsUpdate = new_root.toUpdate();
testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);

eventCaptor = ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mockParent, times(4))
.requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
event = eventCaptor.getAllValues().get(2);
assertEquals(event.getEventType(), AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
sentences = event.getText();
assertEquals(sentences.size(), 1);
assertEquals(sentences.get(0).toString(), "new_node2");
verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("new_node2"));
}

@Test
Expand Down Expand Up @@ -517,12 +502,7 @@ public void itIgnoresUnfocusableNodeDuringHitTest() {
TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);

ArgumentCaptor<AccessibilityEvent> eventCaptor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mockParent, times(2))
.requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
AccessibilityEvent event = eventCaptor.getAllValues().get(0);
assertEquals(event.getEventType(), AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq(" "));

// Synthesize an accessibility hit test event.
MotionEvent mockEvent = mock(MotionEvent.class);
Expand All @@ -533,10 +513,11 @@ public void itIgnoresUnfocusableNodeDuringHitTest() {

assertEquals(hit, true);

eventCaptor = ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mockParent, times(3))
ArgumentCaptor<AccessibilityEvent> eventCaptor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mockParent, times(2))
.requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
event = eventCaptor.getAllValues().get(2);
AccessibilityEvent event = eventCaptor.getAllValues().get(1);
assertEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
assertEquals(accessibilityBridge.getHoveredObjectId(), 2);
}
Expand Down Expand Up @@ -572,15 +553,7 @@ public void itAnnouncesRouteNameWhenRemoveARoute() {
TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);

ArgumentCaptor<AccessibilityEvent> eventCaptor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mockParent, times(2))
.requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
AccessibilityEvent event = eventCaptor.getAllValues().get(0);
assertEquals(event.getEventType(), AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
List<CharSequence> sentences = event.getText();
assertEquals(sentences.size(), 1);
assertEquals(sentences.get(0).toString(), "node2");
verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("node2"));

TestSemanticsNode new_root = new TestSemanticsNode();
new_root.id = 0;
Expand All @@ -597,14 +570,7 @@ public void itAnnouncesRouteNameWhenRemoveARoute() {
testSemanticsUpdate = new_root.toUpdate();
testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);

eventCaptor = ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mockParent, times(4))
.requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
event = eventCaptor.getAllValues().get(2);
assertEquals(event.getEventType(), AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
sentences = event.getText();
assertEquals(sentences.size(), 1);
assertEquals(sentences.get(0).toString(), "new_node2");
verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("new_node2"));
}

@TargetApi(21)
Expand Down Expand Up @@ -1087,15 +1053,7 @@ public void itAnnouncesWhiteSpaceWhenNoNamesRoute() {
TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);

ArgumentCaptor<AccessibilityEvent> eventCaptor =
ArgumentCaptor.forClass(AccessibilityEvent.class);
verify(mockParent, times(2))
.requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
AccessibilityEvent event = eventCaptor.getAllValues().get(0);
assertEquals(event.getEventType(), AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
List<CharSequence> sentences = event.getText();
assertEquals(sentences.size(), 1);
assertEquals(sentences.get(0).toString(), " ");
verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq(" "));
}

@Test
Expand Down

0 comments on commit c889b02

Please sign in to comment.