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

Commit ea22916

Browse files
committed
tests
1 parent 7103322 commit ea22916

File tree

4 files changed

+130
-21
lines changed

4 files changed

+130
-21
lines changed

shell/platform/android/io/flutter/view/AccessibilityBridge.java

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
import androidx.annotation.NonNull;
2727
import androidx.annotation.Nullable;
2828
import androidx.annotation.RequiresApi;
29+
import androidx.annotation.VisibleForTesting;
2930
import io.flutter.BuildConfig;
3031
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
3132
import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate;
@@ -333,10 +334,26 @@ public AccessibilityBridge(
333334
// TODO(mattcarrol): Add the annotation once the plumbing is done.
334335
// https://github.com/flutter/flutter/issues/29618
335336
PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate) {
337+
this(rootAccessibilityView, accessibilityChannel, accessibilityManager, contentResolver, new AccessibilityViewEmbedder(rootAccessibilityView, MIN_ENGINE_GENERATED_NODE_ID), platformViewsAccessibilityDelegate);
338+
}
339+
340+
@VisibleForTesting
341+
public AccessibilityBridge(
342+
@NonNull View rootAccessibilityView,
343+
@NonNull AccessibilityChannel accessibilityChannel,
344+
@NonNull AccessibilityManager accessibilityManager,
345+
@NonNull ContentResolver contentResolver,
346+
@NonNull AccessibilityViewEmbedder accessibilityViewEmbedder,
347+
// This should be @NonNull once the plumbing for
348+
// io.flutter.embedding.engine.android.FlutterView is done.
349+
// TODO(mattcarrol): Add the annotation once the plumbing is done.
350+
// https://github.com/flutter/flutter/issues/29618
351+
PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate) {
336352
this.rootAccessibilityView = rootAccessibilityView;
337353
this.accessibilityChannel = accessibilityChannel;
338354
this.accessibilityManager = accessibilityManager;
339355
this.contentResolver = contentResolver;
356+
this.accessibilityViewEmbedder = accessibilityViewEmbedder;
340357
this.platformViewsAccessibilityDelegate = platformViewsAccessibilityDelegate;
341358

342359
// Tell Flutter whether accessibility is initially active or not. Then register a listener
@@ -388,8 +405,6 @@ public void onTouchExplorationStateChanged(boolean isTouchExplorationEnabled) {
388405
if (platformViewsAccessibilityDelegate != null) {
389406
platformViewsAccessibilityDelegate.attachAccessibilityBridge(this);
390407
}
391-
accessibilityViewEmbedder =
392-
new AccessibilityViewEmbedder(rootAccessibilityView, MIN_ENGINE_GENERATED_NODE_ID);
393408
}
394409

395410
/**

shell/platform/android/io/flutter/view/AccessibilityViewEmbedder.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
* corresponding platform view and `originId`.
4545
*/
4646
@Keep
47-
final class AccessibilityViewEmbedder {
47+
class AccessibilityViewEmbedder {
4848
private static final String TAG = "AccessibilityBridge";
4949

5050
private final ReflectionAccessors reflectionAccessors;

shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java

Lines changed: 111 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,27 @@
55
package io.flutter.view;
66

77
import static org.junit.Assert.assertEquals;
8+
import static org.mockito.Matchers.eq;
89
import static org.mockito.Mockito.mock;
10+
import static org.mockito.Mockito.times;
11+
import static org.mockito.Mockito.verify;
912
import static org.mockito.Mockito.when;
1013

1114
import android.content.ContentResolver;
1215
import android.content.Context;
1316
import android.view.View;
17+
import android.view.ViewParent;
18+
import android.view.accessibility.AccessibilityEvent;
1419
import android.view.accessibility.AccessibilityManager;
1520
import android.view.accessibility.AccessibilityNodeInfo;
1621
import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
1722
import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate;
1823
import java.nio.ByteBuffer;
1924
import java.util.ArrayList;
25+
import java.util.List;
2026
import org.junit.Test;
2127
import org.junit.runner.RunWith;
28+
import org.mockito.ArgumentCaptor;
2229
import org.robolectric.RobolectricTestRunner;
2330
import org.robolectric.annotation.Config;
2431

@@ -73,24 +80,95 @@ public void itDoesNotContainADescriptionIfScopesRoute() {
7380
assertEquals(nodeInfo.getText(), null);
7481
}
7582

76-
AccessibilityBridge setUpBridge() {
77-
View view = mock(View.class);
83+
@Test
84+
public void itUnfocusesPlatformViewWhenPlatformViewGoesAway() {
85+
AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
86+
AccessibilityManager mockManager = mock(AccessibilityManager.class);
87+
View mockRootView = mock(View.class);
7888
Context context = mock(Context.class);
79-
when(view.getContext()).thenReturn(context);
89+
when(mockRootView.getContext()).thenReturn(context);
8090
when(context.getPackageName()).thenReturn("test");
81-
AccessibilityChannel accessibilityChannel = mock(AccessibilityChannel.class);
82-
AccessibilityManager accessibilityManager = mock(AccessibilityManager.class);
83-
ContentResolver contentResolver = mock(ContentResolver.class);
84-
PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate =
85-
mock(PlatformViewsAccessibilityDelegate.class);
86-
AccessibilityBridge accessibilityBridge =
87-
new AccessibilityBridge(
88-
view,
91+
AccessibilityBridge accessibilityBridge = setUpBridge(mockRootView, mockManager, mockViewEmbedder);
92+
93+
// Sent a11y tree with platform view.
94+
TestSemanticsNode root = new TestSemanticsNode();
95+
root.id = 0;
96+
TestSemanticsNode platformView = new TestSemanticsNode();
97+
platformView.id = 1;
98+
platformView.platformViewId = 42;
99+
root.children.add(platformView);
100+
TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
101+
accessibilityBridge.updateSemantics(testSemanticsUpdate.buffer, testSemanticsUpdate.strings);
102+
103+
// Set a11y focus to platform view.
104+
View mockView = mock(View.class);
105+
AccessibilityEvent focusEvent = mock(AccessibilityEvent.class);
106+
when(mockViewEmbedder.requestSendAccessibilityEvent(mockView, mockView, focusEvent)).thenReturn(true);
107+
when(mockViewEmbedder.getRecordFlutterId(mockView, focusEvent)).thenReturn(42);
108+
when(focusEvent.getEventType()).thenReturn(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
109+
accessibilityBridge.externalViewRequestSendAccessibilityEvent(mockView, mockView, focusEvent);
110+
111+
// Replace the platform view.
112+
TestSemanticsNode node = new TestSemanticsNode();
113+
node.id = 2;
114+
root.children.clear();
115+
root.children.add(node);
116+
testSemanticsUpdate = root.toUpdate();
117+
when(mockManager.isEnabled()).thenReturn(true);
118+
ViewParent mockParent = mock(ViewParent.class);
119+
when(mockRootView.getParent()).thenReturn(mockParent);
120+
accessibilityBridge.updateSemantics(testSemanticsUpdate.buffer, testSemanticsUpdate.strings);
121+
122+
// Check that unfocus event was sent.
123+
ArgumentCaptor<AccessibilityEvent> eventCaptor = ArgumentCaptor.forClass(AccessibilityEvent.class);
124+
verify(mockParent, times(2)).requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
125+
AccessibilityEvent event = eventCaptor.getAllValues().get(0);
126+
assertEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
127+
}
128+
129+
AccessibilityBridge setUpBridge() {
130+
return setUpBridge(null, null, null, null, null, null);
131+
}
132+
133+
AccessibilityBridge setUpBridge(View rootAccessibilityView, AccessibilityManager accessibilityManager, AccessibilityViewEmbedder accessibilityViewEmbedder) {
134+
return setUpBridge(rootAccessibilityView, null, accessibilityManager, null, accessibilityViewEmbedder, null);
135+
}
136+
137+
AccessibilityBridge setUpBridge(
138+
View rootAccessibilityView,
139+
AccessibilityChannel accessibilityChannel,
140+
AccessibilityManager accessibilityManager,
141+
ContentResolver contentResolver,
142+
AccessibilityViewEmbedder accessibilityViewEmbedder,
143+
PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate
144+
) {
145+
if (rootAccessibilityView == null) {
146+
rootAccessibilityView = mock(View.class);
147+
Context context = mock(Context.class);
148+
when(rootAccessibilityView.getContext()).thenReturn(context);
149+
when(context.getPackageName()).thenReturn("test");
150+
}
151+
if (accessibilityChannel == null) {
152+
accessibilityChannel = mock(AccessibilityChannel.class);
153+
}
154+
if (accessibilityManager == null) {
155+
accessibilityManager = mock(AccessibilityManager.class);
156+
}
157+
if (contentResolver == null) {
158+
contentResolver = mock(ContentResolver.class);
159+
}
160+
if (accessibilityViewEmbedder == null) {
161+
accessibilityViewEmbedder = mock(AccessibilityViewEmbedder.class);
162+
}
163+
if (platformViewsAccessibilityDelegate == null) {
164+
platformViewsAccessibilityDelegate = mock(PlatformViewsAccessibilityDelegate.class);
165+
}
166+
return new AccessibilityBridge(
167+
rootAccessibilityView,
89168
accessibilityChannel,
90169
accessibilityManager,
91170
contentResolver,
92-
platformViewsAccessibilityDelegate);
93-
return accessibilityBridge;
171+
accessibilityViewEmbedder, platformViewsAccessibilityDelegate);
94172
}
95173

96174
/// The encoding for semantics is described in platform_view_android.cc
@@ -136,11 +214,18 @@ void addFlag(AccessibilityBridge.Flag flag) {
136214
float top = 0.0f;
137215
float right = 0.0f;
138216
float bottom = 0.0f;
139-
// children and custom actions not supported.
217+
final List<TestSemanticsNode> children = new ArrayList<TestSemanticsNode>();
218+
//custom actions not supported.
140219

141220
TestSemanticsUpdate toUpdate() {
142221
ArrayList<String> strings = new ArrayList<String>();
143222
ByteBuffer bytes = ByteBuffer.allocate(1000);
223+
addToBuffer(bytes, strings);
224+
bytes.flip();
225+
return new TestSemanticsUpdate(bytes, strings.toArray(new String[strings.size()]));
226+
}
227+
228+
protected void addToBuffer(ByteBuffer bytes, ArrayList<String> strings) {
144229
bytes.putInt(id);
145230
bytes.putInt(flags);
146231
bytes.putInt(actions);
@@ -169,11 +254,20 @@ TestSemanticsUpdate toUpdate() {
169254
bytes.putFloat(0);
170255
}
171256
// children in traversal order.
172-
bytes.putInt(0);
257+
bytes.putInt(children.size());
258+
for (TestSemanticsNode node : children) {
259+
bytes.putInt(node.id);
260+
}
261+
// children in hit test order.
262+
for (TestSemanticsNode node : children) {
263+
bytes.putInt(node.id);
264+
}
173265
// custom actions
174266
bytes.putInt(0);
175-
bytes.flip();
176-
return new TestSemanticsUpdate(bytes, strings.toArray(new String[strings.size()]));
267+
// child nodes
268+
for (TestSemanticsNode node : children) {
269+
node.addToBuffer(bytes, strings);
270+
}
177271
}
178272
}
179273

testing/run_tests.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -282,7 +282,7 @@ def EnsureJavaTestsAreBuilt(android_out_dir):
282282
RunCmd(ninja_command, cwd=buildroot_dir)
283283

284284
def AssertExpectedJavaVersion():
285-
EXPECTED_VERSION = '1.8'
285+
EXPECTED_VERSION = '11.0'
286286
# `java -version` is output to stderr. https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4380614
287287
version_output = subprocess.check_output(['java', '-version'], stderr=subprocess.STDOUT)
288288
match = bool(re.compile('version "%s' % EXPECTED_VERSION).search(version_output))

0 commit comments

Comments
 (0)