Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
AccessibilityBridgeTest.java
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package io.flutter.view;
6
7import static io.flutter.Build.API_LEVELS;
8import static org.junit.Assert.assertEquals;
9import static org.junit.Assert.assertFalse;
10import static org.junit.Assert.assertNotEquals;
11import static org.junit.Assert.assertNotNull;
12import static org.junit.Assert.assertTrue;
13import static org.mockito.ArgumentMatchers.eq;
14import static org.mockito.Mockito.any;
15import static org.mockito.Mockito.anyInt;
16import static org.mockito.Mockito.doAnswer;
17import static org.mockito.Mockito.mock;
18import static org.mockito.Mockito.never;
19import static org.mockito.Mockito.reset;
20import static org.mockito.Mockito.spy;
21import static org.mockito.Mockito.times;
22import static org.mockito.Mockito.verify;
23import static org.mockito.Mockito.when;
24
25import android.annotation.TargetApi;
26import android.app.Activity;
27import android.content.ContentResolver;
28import android.content.Context;
29import android.content.res.Configuration;
30import android.content.res.Resources;
31import android.graphics.Rect;
32import android.os.Bundle;
33import android.text.SpannableString;
34import android.text.SpannedString;
35import android.text.style.LocaleSpan;
36import android.text.style.TtsSpan;
37import android.view.MotionEvent;
38import android.view.View;
39import android.view.ViewParent;
40import android.view.Window;
41import android.view.WindowInsets;
42import android.view.WindowManager;
43import android.view.accessibility.AccessibilityEvent;
44import android.view.accessibility.AccessibilityManager;
45import android.view.accessibility.AccessibilityNodeInfo;
46import androidx.test.core.app.ApplicationProvider;
47import androidx.test.ext.junit.runners.AndroidJUnit4;
48import io.flutter.embedding.engine.FlutterJNI;
49import io.flutter.embedding.engine.dart.DartExecutor;
50import io.flutter.embedding.engine.systemchannels.AccessibilityChannel;
51import io.flutter.plugin.common.BasicMessageChannel;
52import io.flutter.plugin.platform.PlatformViewsAccessibilityDelegate;
53import io.flutter.view.AccessibilityBridge.Action;
54import io.flutter.view.AccessibilityBridge.Flag;
55import java.nio.ByteBuffer;
56import java.nio.charset.Charset;
57import java.util.ArrayList;
58import java.util.HashMap;
59import java.util.List;
60import org.junit.Test;
61import org.junit.runner.RunWith;
62import org.mockito.ArgumentCaptor;
63import org.mockito.invocation.InvocationOnMock;
64import org.robolectric.annotation.Config;
65
66@Config(manifest = Config.NONE)
67@RunWith(AndroidJUnit4.class)
69
70 @Test
72 AccessibilityBridge accessibilityBridge = setUpBridge();
73
74 TestSemanticsNode testSemanticsNode = new TestSemanticsNode();
75 testSemanticsNode.label = "Hello, World";
76 TestSemanticsUpdate testSemanticsUpdate = testSemanticsNode.toUpdate();
77 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
78 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
79
80 assertEquals(nodeInfo.getContentDescription().toString(), "Hello, World");
81 assertEquals(nodeInfo.getText(), null);
82 }
83
84 @Config(sdk = API_LEVELS.API_28)
85 @TargetApi(API_LEVELS.API_28)
86 @Test
88 AccessibilityBridge accessibilityBridge = setUpBridge();
89
90 TestSemanticsNode testSemanticsNode = new TestSemanticsNode();
91 testSemanticsNode.value = "Hello, World";
92 testSemanticsNode.label = "some label";
93 testSemanticsNode.hint = "some hint";
94 testSemanticsNode.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
95 TestSemanticsUpdate testSemanticsUpdate = testSemanticsNode.toUpdate();
96 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
97 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
98
99 assertEquals(nodeInfo.getContentDescription(), null);
100 assertEquals(nodeInfo.getText().toString(), "Hello, World");
101 assertEquals(nodeInfo.getHintText().toString(), "some label, some hint");
102 }
103
104 @Test
106 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
107 AccessibilityManager mockManager = mock(AccessibilityManager.class);
108 View mockRootView = mock(View.class);
109 Context context = mock(Context.class);
110 when(mockRootView.getContext()).thenReturn(context);
111 final int position = 88;
112 // The getBoundsInScreen() in createAccessibilityNodeInfo() needs View.getLocationOnScreen()
113 doAnswer(
114 invocation -> {
115 int[] outLocation = (int[]) invocation.getArguments()[0];
116 outLocation[0] = position;
117 outLocation[1] = position;
118 return null;
119 })
120 .when(mockRootView)
121 .getLocationOnScreen(any(int[].class));
122
123 when(context.getPackageName()).thenReturn("test");
124 AccessibilityBridge accessibilityBridge =
125 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
126
127 TestSemanticsNode testSemanticsNode = new TestSemanticsNode();
128 TestSemanticsUpdate testSemanticsUpdate = testSemanticsNode.toUpdate();
129
130 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
131 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
132
133 Rect outBoundsInScreen = new Rect();
134 nodeInfo.getBoundsInScreen(outBoundsInScreen);
135 assertEquals(position, outBoundsInScreen.left);
136 assertEquals(position, outBoundsInScreen.top);
137 }
138
139 @Test
141 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
142 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
143 AccessibilityManager mockManager = mock(AccessibilityManager.class);
144 View mockRootView = mock(View.class);
145 Context context = mock(Context.class);
146 when(mockRootView.getContext()).thenReturn(context);
147 when(context.getPackageName()).thenReturn("test");
148 when(mockManager.isTouchExplorationEnabled()).thenReturn(false);
149 AccessibilityBridge accessibilityBridge =
150 setUpBridge(
151 /*rootAccessibilityView=*/ mockRootView,
152 /*accessibilityChannel=*/ mockChannel,
153 /*accessibilityManager=*/ mockManager,
154 /*contentResolver=*/ null,
155 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
156 /*platformViewsAccessibilityDelegate=*/ null);
157 ArgumentCaptor<AccessibilityManager.TouchExplorationStateChangeListener> listenerCaptor =
158 ArgumentCaptor.forClass(AccessibilityManager.TouchExplorationStateChangeListener.class);
159 verify(mockManager).addTouchExplorationStateChangeListener(listenerCaptor.capture());
160
161 assertEquals(accessibilityBridge.getAccessibleNavigation(), false);
162 verify(mockChannel).setAccessibilityFeatures(0);
163 reset(mockChannel);
164
165 // Simulate assistive technology accessing accessibility tree.
166 accessibilityBridge.createAccessibilityNodeInfo(0);
167 verify(mockChannel).setAccessibilityFeatures(1);
168 assertEquals(accessibilityBridge.getAccessibleNavigation(), true);
169
170 // Simulate turning off TalkBack.
171 reset(mockChannel);
172 listenerCaptor.getValue().onTouchExplorationStateChanged(false);
173 verify(mockChannel).setAccessibilityFeatures(0);
174 assertEquals(accessibilityBridge.getAccessibleNavigation(), false);
175 }
176
177 @Test
179 AccessibilityBridge accessibilityBridge = setUpBridge();
180
181 TestSemanticsNode testSemanticsNode = new TestSemanticsNode();
182 testSemanticsNode.label = "Hello, World";
183 testSemanticsNode.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
184 TestSemanticsUpdate testSemanticsUpdate = testSemanticsNode.toUpdate();
185
186 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
187 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
188
189 assertEquals(nodeInfo.getContentDescription(), null);
190 assertEquals(nodeInfo.getText(), null);
191 }
192
193 @Test
195 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
196 AccessibilityManager mockManager = mock(AccessibilityManager.class);
197 View mockRootView = mock(View.class);
198 Context context = mock(Context.class);
199 when(mockRootView.getContext()).thenReturn(context);
200 when(context.getPackageName()).thenReturn("test");
201 AccessibilityBridge accessibilityBridge =
202 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
203
204 // Sent a11y tree with platform view.
206 root.id = 0;
207 TestSemanticsNode platformView = new TestSemanticsNode();
208 platformView.id = 1;
209 platformView.platformViewId = 42;
210 root.children.add(platformView);
211 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
212 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
213
214 // Set a11y focus to platform view.
215 View mockView = mock(View.class);
216 AccessibilityEvent focusEvent = mock(AccessibilityEvent.class);
217 when(mockViewEmbedder.requestSendAccessibilityEvent(mockView, mockView, focusEvent))
218 .thenReturn(true);
219 when(mockViewEmbedder.getRecordFlutterId(mockView, focusEvent)).thenReturn(42);
220 when(focusEvent.getEventType()).thenReturn(AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
221 accessibilityBridge.externalViewRequestSendAccessibilityEvent(mockView, mockView, focusEvent);
222
223 // Replace the platform view.
225 node.id = 2;
226 root.children.clear();
227 root.children.add(node);
228 testSemanticsUpdate = root.toUpdate();
229 when(mockManager.isEnabled()).thenReturn(true);
230 ViewParent mockParent = mock(ViewParent.class);
231 when(mockRootView.getParent()).thenReturn(mockParent);
232 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
233
234 // Check that unfocus event was sent.
235 ArgumentCaptor<AccessibilityEvent> eventCaptor =
236 ArgumentCaptor.forClass(AccessibilityEvent.class);
237 verify(mockParent, times(2))
238 .requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
239 AccessibilityEvent event = eventCaptor.getAllValues().get(0);
240 assertEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
241 }
242
243 @Test
245 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
246 AccessibilityManager mockManager = mock(AccessibilityManager.class);
247 View mockRootView = mock(View.class);
248 Context context = mock(Context.class);
249 when(mockRootView.getContext()).thenReturn(context);
250 when(context.getPackageName()).thenReturn("test");
251 AccessibilityBridge accessibilityBridge =
252 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
253 ViewParent mockParent = mock(ViewParent.class);
254 when(mockRootView.getParent()).thenReturn(mockParent);
255 when(mockManager.isEnabled()).thenReturn(true);
256
258 root.id = 0;
260 node1.id = 1;
261 node1.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
262 node1.addFlag(AccessibilityBridge.Flag.NAMES_ROUTE);
263 node1.label = "node1";
264 root.children.add(node1);
265 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
266 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
267
268 verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("node1"));
269
270 TestSemanticsNode new_root = new TestSemanticsNode();
271 new_root.id = 0;
272 TestSemanticsNode new_node1 = new TestSemanticsNode();
273 new_node1.id = 1;
274 new_node1.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
275 new_node1.addFlag(AccessibilityBridge.Flag.NAMES_ROUTE);
276 new_node1.label = "new_node1";
277 new_root.children.add(new_node1);
278 TestSemanticsNode new_node2 = new TestSemanticsNode();
279 new_node2.id = 2;
280 new_node2.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
281 new_node2.addFlag(AccessibilityBridge.Flag.NAMES_ROUTE);
282 new_node2.label = "new_node2";
283 new_node1.children.add(new_node2);
284 testSemanticsUpdate = new_root.toUpdate();
285 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
286
287 verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("new_node2"));
288 }
289
290 @Test
291 public void itSetsTraversalAfter() {
292 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
293 AccessibilityManager mockManager = mock(AccessibilityManager.class);
294 View mockRootView = mock(View.class);
295 Context context = mock(Context.class);
296 when(mockRootView.getContext()).thenReturn(context);
297 when(context.getPackageName()).thenReturn("test");
298 AccessibilityBridge accessibilityBridge =
299 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
300 ViewParent mockParent = mock(ViewParent.class);
301 when(mockRootView.getParent()).thenReturn(mockParent);
302 when(mockManager.isEnabled()).thenReturn(true);
303
305 root.id = 0;
307 node1.id = 1;
308 node1.label = "node1";
309 root.children.add(node1);
311 node2.id = 2;
312 node2.label = "node2";
313 root.children.add(node2);
314 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
315 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
316
317 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
318 AccessibilityNodeInfo mockNodeInfo2 = mock(AccessibilityNodeInfo.class);
319
320 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 2))
321 .thenReturn(mockNodeInfo2);
322 spyAccessibilityBridge.createAccessibilityNodeInfo(2);
323 verify(mockNodeInfo2, times(1)).setTraversalAfter(eq(mockRootView), eq(1));
324 }
325
326 @Config(sdk = API_LEVELS.API_24)
327 @TargetApi(API_LEVELS.API_24)
328 @Test
330 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
331 AccessibilityManager mockManager = mock(AccessibilityManager.class);
332 View mockRootView = mock(View.class);
333 Context context = mock(Context.class);
334 when(mockRootView.getContext()).thenReturn(context);
335 when(context.getPackageName()).thenReturn("test");
336 AccessibilityBridge accessibilityBridge =
337 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
338 ViewParent mockParent = mock(ViewParent.class);
339 when(mockRootView.getParent()).thenReturn(mockParent);
340 when(mockManager.isEnabled()).thenReturn(true);
341
343 root.id = 0;
344 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
345 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
346
347 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
348 AccessibilityNodeInfo mockNodeInfo = mock(AccessibilityNodeInfo.class);
349
350 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView)).thenReturn(mockNodeInfo);
351 spyAccessibilityBridge.createAccessibilityNodeInfo(View.NO_ID);
352 verify(mockNodeInfo, times(1)).setImportantForAccessibility(eq(false));
353 }
354
355 @Config(sdk = API_LEVELS.API_24)
356 @TargetApi(API_LEVELS.API_24)
357 @Test
359 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
360 AccessibilityManager mockManager = mock(AccessibilityManager.class);
361 View mockRootView = mock(View.class);
362 Context context = mock(Context.class);
363 when(mockRootView.getContext()).thenReturn(context);
364 when(context.getPackageName()).thenReturn("test");
365 AccessibilityBridge accessibilityBridge =
366 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
367 ViewParent mockParent = mock(ViewParent.class);
368 when(mockRootView.getParent()).thenReturn(mockParent);
369 when(mockManager.isEnabled()).thenReturn(true);
370
372 root.id = 0;
373 root.label = "some label";
374 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
375 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
376
377 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
378 AccessibilityNodeInfo mockNodeInfo = mock(AccessibilityNodeInfo.class);
379
380 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 0))
381 .thenReturn(mockNodeInfo);
382 spyAccessibilityBridge.createAccessibilityNodeInfo(0);
383 verify(mockNodeInfo, times(1)).setImportantForAccessibility(eq(true));
384 }
385
386 @Config(sdk = API_LEVELS.API_24)
387 @TargetApi(API_LEVELS.API_24)
388 @Test
390 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
391 AccessibilityManager mockManager = mock(AccessibilityManager.class);
392 View mockRootView = mock(View.class);
393 Context context = mock(Context.class);
394 when(mockRootView.getContext()).thenReturn(context);
395 when(context.getPackageName()).thenReturn("test");
396 AccessibilityBridge accessibilityBridge =
397 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
398 ViewParent mockParent = mock(ViewParent.class);
399 when(mockRootView.getParent()).thenReturn(mockParent);
400 when(mockManager.isEnabled()).thenReturn(true);
401
403 root.id = 0;
404 root.addAction(Action.TAP);
405 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
406 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
407
408 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
409 AccessibilityNodeInfo mockNodeInfo = mock(AccessibilityNodeInfo.class);
410
411 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 0))
412 .thenReturn(mockNodeInfo);
413 spyAccessibilityBridge.createAccessibilityNodeInfo(0);
414 verify(mockNodeInfo, times(1)).setImportantForAccessibility(eq(true));
415 }
416
417 @Config(sdk = API_LEVELS.API_24)
418 @TargetApi(API_LEVELS.API_24)
419 @Test
421 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
422 AccessibilityManager mockManager = mock(AccessibilityManager.class);
423 View mockRootView = mock(View.class);
424 Context context = mock(Context.class);
425 when(mockRootView.getContext()).thenReturn(context);
426 when(context.getPackageName()).thenReturn("test");
427 AccessibilityBridge accessibilityBridge =
428 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
429 ViewParent mockParent = mock(ViewParent.class);
430 when(mockRootView.getParent()).thenReturn(mockParent);
431 when(mockManager.isEnabled()).thenReturn(true);
432
434 root.id = 0;
436 node.id = 1;
437 root.children.add(node);
438 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
439 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
440
441 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
442 AccessibilityNodeInfo mockNodeInfo = mock(AccessibilityNodeInfo.class);
443
444 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 0))
445 .thenReturn(mockNodeInfo);
446 spyAccessibilityBridge.createAccessibilityNodeInfo(0);
447 verify(mockNodeInfo, times(1)).setImportantForAccessibility(eq(false));
448
449 AccessibilityNodeInfo mockNodeInfo1 = mock(AccessibilityNodeInfo.class);
450
451 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 1))
452 .thenReturn(mockNodeInfo1);
453 spyAccessibilityBridge.createAccessibilityNodeInfo(1);
454 verify(mockNodeInfo1, times(1)).setImportantForAccessibility(eq(false));
455 }
456
457 @SuppressWarnings("deprecation")
458 // getSystemWindowInset* methods deprecated.
459 @Config(sdk = API_LEVELS.API_28)
460 @TargetApi(API_LEVELS.API_28)
461 @Test
463 int expectedInsetLeft = 5;
464 int top = 0;
465 int left = 0;
466 int right = 100;
467 int bottom = 200;
468 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
469 AccessibilityManager mockManager = mock(AccessibilityManager.class);
470 View mockRootView = mock(View.class);
471 Activity context = mock(Activity.class);
472 Window window = mock(Window.class);
473 WindowInsets insets = mock(WindowInsets.class);
474 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
475 layoutParams.layoutInDisplayCutoutMode =
476 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
477 when(mockRootView.getContext()).thenReturn(context);
478 when(context.getWindow()).thenReturn(window);
479 when(window.getAttributes()).thenReturn(layoutParams);
480 when(mockRootView.getRootWindowInsets()).thenReturn(insets);
481 when(insets.getSystemWindowInsetLeft()).thenReturn(expectedInsetLeft);
482 when(context.getPackageName()).thenReturn("test");
483 AccessibilityBridge accessibilityBridge =
484 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
485 ViewParent mockParent = mock(ViewParent.class);
486 when(mockRootView.getParent()).thenReturn(mockParent);
487 when(mockManager.isEnabled()).thenReturn(true);
488
490 root.id = 0;
491 root.left = left;
492 root.top = top;
493 root.right = right;
494 root.bottom = bottom;
495 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
496 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
497
498 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
499 AccessibilityNodeInfo mockNodeInfo = mock(AccessibilityNodeInfo.class);
500
501 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 0))
502 .thenReturn(mockNodeInfo);
503 spyAccessibilityBridge.createAccessibilityNodeInfo(0);
504 verify(mockNodeInfo, times(1))
505 .setBoundsInScreen(
506 new Rect(left + expectedInsetLeft, top, right + expectedInsetLeft, bottom));
507 }
508
509 @SuppressWarnings("deprecation")
510 // getSystemWindowInset* methods deprecated.
511 @Config(sdk = API_LEVELS.API_28)
512 @TargetApi(API_LEVELS.API_28)
513 @Test
515 int expectedInsetLeft = 5;
516 int top = 0;
517 int left = 0;
518 int right = 100;
519 int bottom = 200;
520 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
521 AccessibilityManager mockManager = mock(AccessibilityManager.class);
522 View mockRootView = mock(View.class);
523 Activity context = mock(Activity.class);
524 Window window = mock(Window.class);
525 WindowInsets insets = mock(WindowInsets.class);
526 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
527 layoutParams.layoutInDisplayCutoutMode =
528 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT;
529 when(mockRootView.getContext()).thenReturn(context);
530 when(context.getWindow()).thenReturn(window);
531 when(window.getAttributes()).thenReturn(layoutParams);
532 when(mockRootView.getRootWindowInsets()).thenReturn(insets);
533 when(insets.getSystemWindowInsetLeft()).thenReturn(expectedInsetLeft);
534 when(context.getPackageName()).thenReturn("test");
535 AccessibilityBridge accessibilityBridge =
536 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
537 ViewParent mockParent = mock(ViewParent.class);
538 when(mockRootView.getParent()).thenReturn(mockParent);
539 when(mockManager.isEnabled()).thenReturn(true);
540
542 root.id = 0;
543 root.left = left;
544 root.top = top;
545 root.right = right;
546 root.bottom = bottom;
547 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
548 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
549
550 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
551 AccessibilityNodeInfo mockNodeInfo = mock(AccessibilityNodeInfo.class);
552
553 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 0))
554 .thenReturn(mockNodeInfo);
555 spyAccessibilityBridge.createAccessibilityNodeInfo(0);
556 verify(mockNodeInfo, times(1))
557 .setBoundsInScreen(
558 new Rect(left + expectedInsetLeft, top, right + expectedInsetLeft, bottom));
559 }
560
561 @SuppressWarnings("deprecation")
562 // getSystemWindowInset* methods deprecated.
563 @Config(sdk = API_LEVELS.API_28)
564 @TargetApi(API_LEVELS.API_28)
565 @Test
567 int expectedInsetLeft = 5;
568 int top = 0;
569 int left = 0;
570 int right = 100;
571 int bottom = 200;
572 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
573 AccessibilityManager mockManager = mock(AccessibilityManager.class);
574 View mockRootView = mock(View.class);
575 Activity context = mock(Activity.class);
576 Window window = mock(Window.class);
577 WindowInsets insets = mock(WindowInsets.class);
578 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
579 layoutParams.layoutInDisplayCutoutMode =
580 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
581 when(mockRootView.getContext()).thenReturn(context);
582 when(context.getWindow()).thenReturn(window);
583 when(window.getAttributes()).thenReturn(layoutParams);
584 when(mockRootView.getRootWindowInsets()).thenReturn(insets);
585 when(insets.getSystemWindowInsetLeft()).thenReturn(expectedInsetLeft);
586 when(context.getPackageName()).thenReturn("test");
587 AccessibilityBridge accessibilityBridge =
588 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
589 ViewParent mockParent = mock(ViewParent.class);
590 when(mockRootView.getParent()).thenReturn(mockParent);
591 when(mockManager.isEnabled()).thenReturn(true);
592
594 root.id = 0;
595 root.left = left;
596 root.top = top;
597 root.right = right;
598 root.bottom = bottom;
599 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
600 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
601
602 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
603 AccessibilityNodeInfo mockNodeInfo = mock(AccessibilityNodeInfo.class);
604
605 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 0))
606 .thenReturn(mockNodeInfo);
607 spyAccessibilityBridge.createAccessibilityNodeInfo(0);
608 // Does not apply left inset if the layout mode is `short edges`.
609 verify(mockNodeInfo, times(1)).setBoundsInScreen(new Rect(left, top, right, bottom));
610 }
611
612 @SuppressWarnings("deprecation")
613 // getSystemWindowInset* methods deprecated.
614 // fluter#133074 tracks post deprecation work.
615 @Config(sdk = API_LEVELS.API_30)
616 @TargetApi(API_LEVELS.API_30)
617 @Test
619 int expectedInsetLeft = 5;
620 int top = 0;
621 int left = 0;
622 int right = 100;
623 int bottom = 200;
624 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
625 AccessibilityManager mockManager = mock(AccessibilityManager.class);
626 View mockRootView = mock(View.class);
627 Activity context = mock(Activity.class);
628 Window window = mock(Window.class);
629 WindowInsets insets = mock(WindowInsets.class);
630 WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
631 layoutParams.layoutInDisplayCutoutMode =
632 WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
633 when(mockRootView.getContext()).thenReturn(context);
634 when(context.getWindow()).thenReturn(window);
635 when(window.getAttributes()).thenReturn(layoutParams);
636 when(mockRootView.getRootWindowInsets()).thenReturn(insets);
637 when(insets.getSystemWindowInsetLeft()).thenReturn(expectedInsetLeft);
638 when(context.getPackageName()).thenReturn("test");
639 AccessibilityBridge accessibilityBridge =
640 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
641 ViewParent mockParent = mock(ViewParent.class);
642 when(mockRootView.getParent()).thenReturn(mockParent);
643 when(mockManager.isEnabled()).thenReturn(true);
644
646 root.id = 0;
647 root.left = left;
648 root.top = top;
649 root.right = right;
650 root.bottom = bottom;
651 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
652 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
653
654 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
655 AccessibilityNodeInfo mockNodeInfo = mock(AccessibilityNodeInfo.class);
656
657 when(spyAccessibilityBridge.obtainAccessibilityNodeInfo(mockRootView, 0))
658 .thenReturn(mockNodeInfo);
659 spyAccessibilityBridge.createAccessibilityNodeInfo(0);
660 // Does not apply left inset if the layout mode is `always`.
661 verify(mockNodeInfo, times(1)).setBoundsInScreen(new Rect(left, top, right, bottom));
662 }
663
664 @Test
666 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
667 AccessibilityManager mockManager = mock(AccessibilityManager.class);
668 View mockRootView = mock(View.class);
669 Context context = mock(Context.class);
670 when(mockRootView.getContext()).thenReturn(context);
671 when(context.getPackageName()).thenReturn("test");
672 AccessibilityBridge accessibilityBridge =
673 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
674 ViewParent mockParent = mock(ViewParent.class);
675 when(mockRootView.getParent()).thenReturn(mockParent);
676 when(mockManager.isEnabled()).thenReturn(true);
677 when(mockManager.isTouchExplorationEnabled()).thenReturn(true);
678
680 root.id = 0;
681 root.left = 0;
682 root.top = 0;
683 root.bottom = 20;
684 root.right = 20;
685 TestSemanticsNode ignored = new TestSemanticsNode();
686 ignored.id = 1;
687 ignored.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
688 ignored.left = 0;
689 ignored.top = 0;
690 ignored.bottom = 20;
691 ignored.right = 20;
692 root.children.add(ignored);
694 child.id = 2;
695 child.label = "label";
696 child.left = 0;
697 child.top = 0;
698 child.bottom = 20;
699 child.right = 20;
700 root.children.add(child);
701 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
702 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
703
704 verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq(" "));
705
706 // Synthesize an accessibility hit test event.
707 MotionEvent mockEvent = mock(MotionEvent.class);
708 when(mockEvent.getX()).thenReturn(10.0f);
709 when(mockEvent.getY()).thenReturn(10.0f);
710 when(mockEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
711 boolean hit = accessibilityBridge.onAccessibilityHoverEvent(mockEvent);
712
713 assertEquals(hit, true);
714
715 ArgumentCaptor<AccessibilityEvent> eventCaptor =
716 ArgumentCaptor.forClass(AccessibilityEvent.class);
717 verify(mockParent, times(2))
718 .requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
719 AccessibilityEvent event = eventCaptor.getAllValues().get(1);
720 assertEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_HOVER_ENTER);
721 assertEquals(accessibilityBridge.getHoveredObjectId(), 2);
722 }
723
724 @Test
726 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
727 AccessibilityManager mockManager = mock(AccessibilityManager.class);
728 View mockRootView = mock(View.class);
729 Context context = mock(Context.class);
730 when(mockRootView.getContext()).thenReturn(context);
731 when(context.getPackageName()).thenReturn("test");
732 AccessibilityBridge accessibilityBridge =
733 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
734 ViewParent mockParent = mock(ViewParent.class);
735 when(mockRootView.getParent()).thenReturn(mockParent);
736 when(mockManager.isEnabled()).thenReturn(true);
737 when(mockManager.isTouchExplorationEnabled()).thenReturn(true);
738
740 root.id = 0;
741 root.left = 0;
742 root.top = 0;
743 root.bottom = 20;
744 root.right = 20;
745 TestSemanticsNode platformView = new TestSemanticsNode();
746 platformView.id = 1;
747 platformView.platformViewId = 1;
748 platformView.left = 0;
749 platformView.top = 0;
750 platformView.bottom = 20;
751 platformView.right = 20;
752 root.addChild(platformView);
753 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
754 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
755
756 // Synthesize an accessibility hit test event.
757 MotionEvent mockEvent = mock(MotionEvent.class);
758 when(mockEvent.getX()).thenReturn(10.0f);
759 when(mockEvent.getY()).thenReturn(10.0f);
760 when(mockEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
761
762 final boolean handled = accessibilityBridge.onAccessibilityHoverEvent(mockEvent);
763
764 assertTrue(handled);
765 }
766
767 @Test
769 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
770 AccessibilityManager mockManager = mock(AccessibilityManager.class);
771 View mockRootView = mock(View.class);
772 Context context = mock(Context.class);
773 when(mockRootView.getContext()).thenReturn(context);
774 when(context.getPackageName()).thenReturn("test");
775 AccessibilityBridge accessibilityBridge =
776 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
777 ViewParent mockParent = mock(ViewParent.class);
778 when(mockRootView.getParent()).thenReturn(mockParent);
779 when(mockManager.isEnabled()).thenReturn(true);
780 when(mockManager.isTouchExplorationEnabled()).thenReturn(true);
781
783 root.id = 0;
784 root.left = 0;
785 root.top = 0;
786 root.bottom = 20;
787 root.right = 20;
788 TestSemanticsNode platformView = new TestSemanticsNode();
789 platformView.id = 1;
790 platformView.platformViewId = 1;
791 platformView.left = 0;
792 platformView.top = 0;
793 platformView.bottom = 20;
794 platformView.right = 20;
795 root.addChild(platformView);
796 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
797 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
798
799 // Synthesize an accessibility hit test event.
800 MotionEvent mockEvent = mock(MotionEvent.class);
801 when(mockEvent.getX()).thenReturn(10.0f);
802 when(mockEvent.getY()).thenReturn(10.0f);
803 when(mockEvent.getAction()).thenReturn(MotionEvent.ACTION_HOVER_ENTER);
804
805 final boolean handled = accessibilityBridge.onAccessibilityHoverEvent(mockEvent, true);
806
807 assertFalse(handled);
808 }
809
810 @Test
812 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
813 AccessibilityManager mockManager = mock(AccessibilityManager.class);
814 View mockRootView = mock(View.class);
815 Context context = mock(Context.class);
816 when(mockRootView.getContext()).thenReturn(context);
817 when(context.getPackageName()).thenReturn("test");
818 AccessibilityBridge accessibilityBridge =
819 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
820 ViewParent mockParent = mock(ViewParent.class);
821 when(mockRootView.getParent()).thenReturn(mockParent);
822 when(mockManager.isEnabled()).thenReturn(true);
823
825 root.id = 0;
827 node1.id = 1;
828 node1.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
829 node1.addFlag(AccessibilityBridge.Flag.NAMES_ROUTE);
830 node1.label = "node1";
831 root.children.add(node1);
833 node2.id = 2;
834 node2.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
835 node2.addFlag(AccessibilityBridge.Flag.NAMES_ROUTE);
836 node2.label = "node2";
837 node1.children.add(node2);
838 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
839 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
840
841 verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("node2"));
842
843 TestSemanticsNode new_root = new TestSemanticsNode();
844 new_root.id = 0;
845 TestSemanticsNode new_node1 = new TestSemanticsNode();
846 new_node1.id = 1;
847 new_node1.label = "new_node1";
848 new_root.children.add(new_node1);
849 TestSemanticsNode new_node2 = new TestSemanticsNode();
850 new_node2.id = 2;
851 new_node2.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
852 new_node2.addFlag(AccessibilityBridge.Flag.NAMES_ROUTE);
853 new_node2.label = "new_node2";
854 new_node1.children.add(new_node2);
855 testSemanticsUpdate = new_root.toUpdate();
856 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
857
858 verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq("new_node2"));
859 }
860
861 @Config(sdk = API_LEVELS.API_21)
862 @Test
863 public void itCanPerformSetText() {
864 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
865 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
866 AccessibilityManager mockManager = mock(AccessibilityManager.class);
867 View mockRootView = mock(View.class);
868 Context context = mock(Context.class);
869 when(mockRootView.getContext()).thenReturn(context);
870 when(context.getPackageName()).thenReturn("test");
871 AccessibilityBridge accessibilityBridge =
872 setUpBridge(
873 /*rootAccessibilityView=*/ mockRootView,
874 /*accessibilityChannel=*/ mockChannel,
875 /*accessibilityManager=*/ mockManager,
876 /*contentResolver=*/ null,
877 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
878 /*platformViewsAccessibilityDelegate=*/ null);
879
880 ViewParent mockParent = mock(ViewParent.class);
881 when(mockRootView.getParent()).thenReturn(mockParent);
882 when(mockManager.isEnabled()).thenReturn(true);
883
885 root.id = 0;
887 node1.id = 1;
888 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
889 root.children.add(node1);
890 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
891 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
892 Bundle bundle = new Bundle();
893 String expectedText = "some string";
894 bundle.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, expectedText);
895 accessibilityBridge.performAction(1, AccessibilityNodeInfo.ACTION_SET_TEXT, bundle);
896 verify(mockChannel)
897 .dispatchSemanticsAction(1, AccessibilityBridge.Action.SET_TEXT, expectedText);
898 }
899
900 @Config(sdk = API_LEVELS.API_21)
901 @Test
902 public void itCanPredictSetText() {
903 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
904 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
905 AccessibilityManager mockManager = mock(AccessibilityManager.class);
906 View mockRootView = mock(View.class);
907 Context context = mock(Context.class);
908 when(mockRootView.getContext()).thenReturn(context);
909 when(context.getPackageName()).thenReturn("test");
910 AccessibilityBridge accessibilityBridge =
911 setUpBridge(
912 /*rootAccessibilityView=*/ mockRootView,
913 /*accessibilityChannel=*/ mockChannel,
914 /*accessibilityManager=*/ mockManager,
915 /*contentResolver=*/ null,
916 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
917 /*platformViewsAccessibilityDelegate=*/ null);
918
919 ViewParent mockParent = mock(ViewParent.class);
920 when(mockRootView.getParent()).thenReturn(mockParent);
921 when(mockManager.isEnabled()).thenReturn(true);
922
924 root.id = 0;
926 node1.id = 1;
927 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
928 root.children.add(node1);
929 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
930 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
931 Bundle bundle = new Bundle();
932 String expectedText = "some string";
933 bundle.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, expectedText);
934 accessibilityBridge.performAction(1, AccessibilityNodeInfo.ACTION_SET_TEXT, bundle);
935 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
936 assertEquals(nodeInfo.getText().toString(), expectedText);
937 }
938
939 @Config(sdk = API_LEVELS.API_21)
940 @Test
942 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
943 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
944 AccessibilityManager mockManager = mock(AccessibilityManager.class);
945 View mockRootView = mock(View.class);
946 Context context = mock(Context.class);
947 when(mockRootView.getContext()).thenReturn(context);
948 when(context.getPackageName()).thenReturn("test");
949 AccessibilityBridge accessibilityBridge =
950 setUpBridge(
951 /*rootAccessibilityView=*/ mockRootView,
952 /*accessibilityChannel=*/ mockChannel,
953 /*accessibilityManager=*/ mockManager,
954 /*contentResolver=*/ null,
955 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
956 /*platformViewsAccessibilityDelegate=*/ null);
957
958 ViewParent mockParent = mock(ViewParent.class);
959 when(mockRootView.getParent()).thenReturn(mockParent);
960 when(mockManager.isEnabled()).thenReturn(true);
961
963 root.id = 0;
964 root.label = "label";
966 attribute.start = 1;
967 attribute.end = 2;
968 attribute.type = TestStringAttributeType.SPELLOUT;
969 root.labelAttributes =
970 new ArrayList<TestStringAttribute>() {
971 {
972 add(attribute);
973 }
974 };
975 root.value = "value";
977 localeAttribute.start = 1;
978 localeAttribute.end = 2;
979 localeAttribute.type = TestStringAttributeType.LOCALE;
980 localeAttribute.locale = "es-MX";
981 root.valueAttributes =
982 new ArrayList<TestStringAttribute>() {
983 {
984 add(localeAttribute);
985 }
986 };
987
988 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
989 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
990 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
991 SpannedString actual = (SpannedString) nodeInfo.getContentDescription();
992 assertEquals(actual.toString(), "value, label");
993 Object[] objectSpans = actual.getSpans(0, actual.length(), Object.class);
994 assertEquals(objectSpans.length, 2);
995 LocaleSpan localeSpan = (LocaleSpan) objectSpans[0];
996 assertEquals(localeSpan.getLocale().toLanguageTag(), "es-MX");
997 assertEquals(actual.getSpanStart(localeSpan), 1);
998 assertEquals(actual.getSpanEnd(localeSpan), 2);
999 TtsSpan spellOutSpan = (TtsSpan) objectSpans[1];
1000 assertEquals(spellOutSpan.getType(), TtsSpan.TYPE_VERBATIM);
1001 assertEquals(actual.getSpanStart(spellOutSpan), 8);
1002 assertEquals(actual.getSpanEnd(spellOutSpan), 9);
1003 }
1004
1005 @Config(sdk = API_LEVELS.API_21)
1006 @Test
1007 public void itSetsTextCorrectly() {
1008 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1009 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1010 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1011 View mockRootView = mock(View.class);
1012 Context context = mock(Context.class);
1013 when(mockRootView.getContext()).thenReturn(context);
1014 when(context.getPackageName()).thenReturn("test");
1015 AccessibilityBridge accessibilityBridge =
1016 setUpBridge(
1017 /*rootAccessibilityView=*/ mockRootView,
1018 /*accessibilityChannel=*/ mockChannel,
1019 /*accessibilityManager=*/ mockManager,
1020 /*contentResolver=*/ null,
1021 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1022 /*platformViewsAccessibilityDelegate=*/ null);
1023
1024 ViewParent mockParent = mock(ViewParent.class);
1025 when(mockRootView.getParent()).thenReturn(mockParent);
1026 when(mockManager.isEnabled()).thenReturn(true);
1027
1029 root.id = 0;
1030 root.value = "value";
1032 attribute.start = 1;
1033 attribute.end = 2;
1034 attribute.type = TestStringAttributeType.SPELLOUT;
1035 root.valueAttributes = new ArrayList<>();
1036 root.valueAttributes.add(attribute);
1037
1038 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1039 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1040 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1041 SpannableString actual = (SpannableString) nodeInfo.getContentDescription();
1042 assertEquals(actual.toString(), "value");
1043 Object[] objectSpans = actual.getSpans(0, actual.length(), Object.class);
1044 assertEquals(objectSpans.length, 1);
1045 TtsSpan spellOutSpan = (TtsSpan) objectSpans[0];
1046 assertEquals(spellOutSpan.getType(), TtsSpan.TYPE_VERBATIM);
1047 assertEquals(actual.getSpanStart(spellOutSpan), 1);
1048 assertEquals(actual.getSpanEnd(spellOutSpan), 2);
1049
1050 // Perform a set text action.
1051 Bundle bundle = new Bundle();
1052 String expectedText = "a";
1053 bundle.putString(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, expectedText);
1054 accessibilityBridge.performAction(0, AccessibilityNodeInfo.ACTION_SET_TEXT, bundle);
1055
1056 // The action should remove the string attributes.
1057 nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1058 actual = (SpannableString) nodeInfo.getContentDescription();
1059 assertEquals(actual.toString(), expectedText);
1060 objectSpans = actual.getSpans(0, actual.length(), Object.class);
1061 assertEquals(objectSpans.length, 0);
1062 }
1063
1064 @Config(sdk = API_LEVELS.API_28)
1065 @TargetApi(API_LEVELS.API_28)
1066 @Test
1068 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1069 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1070 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1071 View mockRootView = mock(View.class);
1072 Context context = mock(Context.class);
1073 when(mockRootView.getContext()).thenReturn(context);
1074 when(context.getPackageName()).thenReturn("test");
1075 AccessibilityBridge accessibilityBridge =
1076 setUpBridge(
1077 /*rootAccessibilityView=*/ mockRootView,
1078 /*accessibilityChannel=*/ mockChannel,
1079 /*accessibilityManager=*/ mockManager,
1080 /*contentResolver=*/ null,
1081 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1082 /*platformViewsAccessibilityDelegate=*/ null);
1083
1084 ViewParent mockParent = mock(ViewParent.class);
1085 when(mockRootView.getParent()).thenReturn(mockParent);
1086 when(mockManager.isEnabled()).thenReturn(true);
1087 // Create a node with tooltip.
1089 root.id = 0;
1090 root.tooltip = "tooltip";
1091
1092 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1093 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1094
1095 // Test the generated AccessibilityNodeInfo for the node we created
1096 // and verify it has correct tooltip text.
1097 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1098 CharSequence actual = nodeInfo.getTooltipText();
1099 assertEquals(actual.toString(), root.tooltip);
1100 }
1101
1102 @TargetApi(API_LEVELS.API_28)
1103 @Test
1105 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1106 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1107 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1108 View mockRootView = mock(View.class);
1109 Context context = mock(Context.class);
1110 when(mockRootView.getContext()).thenReturn(context);
1111 when(context.getPackageName()).thenReturn("test");
1112 AccessibilityBridge accessibilityBridge =
1113 setUpBridge(
1114 /*rootAccessibilityView=*/ mockRootView,
1115 /*accessibilityChannel=*/ mockChannel,
1116 /*accessibilityManager=*/ mockManager,
1117 /*contentResolver=*/ null,
1118 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1119 /*platformViewsAccessibilityDelegate=*/ null);
1120
1121 ViewParent mockParent = mock(ViewParent.class);
1122 when(mockRootView.getParent()).thenReturn(mockParent);
1123 when(mockManager.isEnabled()).thenReturn(true);
1124 // Create a node with identifier.
1126 root.id = 0;
1127 root.identifier = "identifier";
1128
1129 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1130 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1131
1132 // Test the generated AccessibilityNodeInfo for the node we created and
1133 // verify it has correct identifier (i.e. resource-id per Android
1134 // terminology).
1135 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1136 CharSequence actual = nodeInfo.getViewIdResourceName();
1137 assertEquals(actual.toString(), root.identifier);
1138 }
1139
1140 @Config(sdk = API_LEVELS.API_21)
1141 @Test
1143 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1144 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1145 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1146 View mockRootView = mock(View.class);
1147 Context context = mock(Context.class);
1148 when(mockRootView.getContext()).thenReturn(context);
1149 when(context.getPackageName()).thenReturn("test");
1150 AccessibilityBridge accessibilityBridge =
1151 setUpBridge(
1152 /*rootAccessibilityView=*/ mockRootView,
1153 /*accessibilityChannel=*/ mockChannel,
1154 /*accessibilityManager=*/ mockManager,
1155 /*contentResolver=*/ null,
1156 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1157 /*platformViewsAccessibilityDelegate=*/ null);
1158
1159 ViewParent mockParent = mock(ViewParent.class);
1160 when(mockRootView.getParent()).thenReturn(mockParent);
1161 when(mockManager.isEnabled()).thenReturn(true);
1162
1164 root.id = 0;
1166 node1.id = 1;
1167 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
1168 node1.addAction(AccessibilityBridge.Action.SET_TEXT);
1169 root.children.add(node1);
1170 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1171 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1172 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1173 List<AccessibilityNodeInfo.AccessibilityAction> actions = nodeInfo.getActionList();
1174 assertTrue(actions.contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_TEXT));
1175 }
1176
1177 @Test
1179 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1180 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1181 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1182 View mockRootView = mock(View.class);
1183 Context context = mock(Context.class);
1184 when(mockRootView.getContext()).thenReturn(context);
1185 when(context.getPackageName()).thenReturn("test");
1186 AccessibilityBridge accessibilityBridge =
1187 setUpBridge(
1188 /*rootAccessibilityView=*/ mockRootView,
1189 /*accessibilityChannel=*/ mockChannel,
1190 /*accessibilityManager=*/ mockManager,
1191 /*contentResolver=*/ null,
1192 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1193 /*platformViewsAccessibilityDelegate=*/ null);
1194
1195 ViewParent mockParent = mock(ViewParent.class);
1196 when(mockRootView.getParent()).thenReturn(mockParent);
1197 when(mockManager.isEnabled()).thenReturn(true);
1198
1200 root.id = 0;
1202 node1.id = 1;
1203 node1.value = "some text";
1204 node1.textSelectionBase = -1;
1205 node1.textSelectionExtent = -1;
1206 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
1207 root.children.add(node1);
1208 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1209 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1210 Bundle bundle = new Bundle();
1211 int expectedStart = 1;
1212 int expectedEnd = 3;
1213 bundle.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, expectedStart);
1214 bundle.putInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, expectedEnd);
1215 accessibilityBridge.performAction(1, AccessibilityNodeInfo.ACTION_SET_SELECTION, bundle);
1216 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1217 assertEquals(nodeInfo.getTextSelectionStart(), expectedStart);
1218 assertEquals(nodeInfo.getTextSelectionEnd(), expectedEnd);
1219 }
1220
1221 @Test
1223 BasicMessageChannel mockChannel = mock(BasicMessageChannel.class);
1224 AccessibilityChannel accessibilityChannel =
1225 new AccessibilityChannel(mockChannel, mock(FlutterJNI.class));
1226 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1227 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1228 View mockRootView = mock(View.class);
1229 Context context = mock(Context.class);
1230 when(mockRootView.getContext()).thenReturn(context);
1231 when(context.getPackageName()).thenReturn("test");
1232 AccessibilityBridge accessibilityBridge =
1233 setUpBridge(
1234 /*rootAccessibilityView=*/ mockRootView,
1235 /*accessibilityChannel=*/ accessibilityChannel,
1236 /*accessibilityManager=*/ mockManager,
1237 /*contentResolver=*/ null,
1238 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1239 /*platformViewsAccessibilityDelegate=*/ null);
1240
1241 ViewParent mockParent = mock(ViewParent.class);
1242 when(mockRootView.getParent()).thenReturn(mockParent);
1243 when(mockManager.isEnabled()).thenReturn(true);
1244
1246 root.id = 0;
1247 root.label = "root";
1249 node1.id = 1;
1250 node1.value = "some text";
1251 root.children.add(node1);
1252
1253 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1254 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1255 accessibilityBridge.performAction(0, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
1256 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1257 assertTrue(nodeInfo.isAccessibilityFocused());
1258
1259 HashMap<String, Object> message = new HashMap<>();
1260 message.put("type", "didGainFocus");
1261 message.put("nodeId", 0);
1262 verify(mockChannel).send(message);
1263 // Clear focus on non-focused node shouldn't do anything
1264 accessibilityBridge.performAction(
1265 1, AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
1266 nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1267 assertTrue(nodeInfo.isAccessibilityFocused());
1268
1269 // Now, clear the focus for real.
1270 accessibilityBridge.performAction(
1271 0, AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
1272 nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1273 assertFalse(nodeInfo.isAccessibilityFocused());
1274 }
1275
1276 @Test
1278 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1279 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1280 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1281 View mockRootView = mock(View.class);
1282 Context context = mock(Context.class);
1283 when(mockRootView.getContext()).thenReturn(context);
1284 when(context.getPackageName()).thenReturn("test");
1285 AccessibilityBridge accessibilityBridge =
1286 setUpBridge(
1287 /*rootAccessibilityView=*/ mockRootView,
1288 /*accessibilityChannel=*/ mockChannel,
1289 /*accessibilityManager=*/ mockManager,
1290 /*contentResolver=*/ null,
1291 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1292 /*platformViewsAccessibilityDelegate=*/ null);
1293
1294 ViewParent mockParent = mock(ViewParent.class);
1295 when(mockRootView.getParent()).thenReturn(mockParent);
1296 when(mockManager.isEnabled()).thenReturn(true);
1297
1299 root.id = 0;
1300 root.addFlag(Flag.HAS_IMPLICIT_SCROLLING);
1302 node1.id = 1;
1303 node1.addFlag(Flag.IS_READ_ONLY);
1304 root.children.add(node1);
1306 node2.id = 2;
1307 node2.addFlag(Flag.HAS_CHECKED_STATE);
1308 root.children.add(node2);
1309 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1310 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1311
1312 // Only node 2 is focusable because it has a flag that is not in
1313 // AccessibilityBridge.TRIVIAL_FLAGS.
1314 AccessibilityNodeInfo rootInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1315 assertFalse(rootInfo.isFocusable());
1316 AccessibilityNodeInfo node1Info = accessibilityBridge.createAccessibilityNodeInfo(1);
1317 assertFalse(node1Info.isFocusable());
1318 AccessibilityNodeInfo node2Info = accessibilityBridge.createAccessibilityNodeInfo(2);
1319 assertTrue(node2Info.isFocusable());
1320 }
1321
1322 @Config(sdk = API_LEVELS.API_31)
1323 @TargetApi(API_LEVELS.API_31)
1324 @Test
1326 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1327 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1328 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1329 View mockRootView = mock(View.class);
1330 Context context = mock(Context.class);
1331 Resources resource = mock(Resources.class);
1332 Configuration config = new Configuration();
1333 config.fontWeightAdjustment = 300;
1334
1335 when(mockRootView.getContext()).thenReturn(context);
1336 when(mockRootView.getResources()).thenReturn(resource);
1337 when(resource.getConfiguration()).thenReturn(config);
1338
1339 AccessibilityBridge accessibilityBridge =
1340 setUpBridge(
1341 /*rootAccessibilityView=*/ mockRootView,
1342 /*accessibilityChannel=*/ mockChannel,
1343 /*accessibilityManager=*/ mockManager,
1344 /*contentResolver=*/ null,
1345 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1346 /*platformViewsAccessibilityDelegate=*/ null);
1347
1348 verify(mockChannel).setAccessibilityFeatures(1 << 3);
1349 }
1350
1351 @Test
1353 BasicMessageChannel mockChannel = mock(BasicMessageChannel.class);
1354 AccessibilityChannel accessibilityChannel =
1355 new AccessibilityChannel(mockChannel, mock(FlutterJNI.class));
1356
1357 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1358 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1359 View mockRootView = mock(View.class);
1360 Context context = mock(Context.class);
1361 when(mockRootView.getContext()).thenReturn(context);
1362 when(context.getPackageName()).thenReturn("test");
1363 AccessibilityBridge accessibilityBridge =
1364 setUpBridge(
1365 /*rootAccessibilityView=*/ mockRootView,
1366 /*accessibilityChannel=*/ accessibilityChannel,
1367 /*accessibilityManager=*/ mockManager,
1368 /*contentResolver=*/ null,
1369 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1370 /*platformViewsAccessibilityDelegate=*/ null);
1371
1372 ViewParent mockParent = mock(ViewParent.class);
1373 when(mockRootView.getParent()).thenReturn(mockParent);
1374 when(mockManager.isEnabled()).thenReturn(true);
1375
1377 root.id = 0;
1378 root.label = "root";
1379
1380 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1381 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1382
1383 class Verifier {
1384 public Verifier(AccessibilityBridge accessibilityBridge) {
1385 this.accessibilityBridge = accessibilityBridge;
1386 }
1387
1388 public AccessibilityBridge accessibilityBridge;
1389 public boolean verified = false;
1390
1391 public boolean verify(InvocationOnMock invocation) {
1392 AccessibilityEvent event = (AccessibilityEvent) invocation.getArguments()[1];
1393 assertEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);
1394 // The accessibility focus must be set before sending out
1395 // the TYPE_VIEW_ACCESSIBILITY_FOCUSED event.
1396 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1397 assertTrue(nodeInfo.isAccessibilityFocused());
1398 verified = true;
1399 return true;
1400 }
1401 };
1402 Verifier verifier = new Verifier(accessibilityBridge);
1403 when(mockParent.requestSendAccessibilityEvent(eq(mockRootView), any(AccessibilityEvent.class)))
1404 .thenAnswer(invocation -> verifier.verify(invocation));
1405 accessibilityBridge.performAction(0, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
1406 assertTrue(verifier.verified);
1407
1408 HashMap<String, Object> message = new HashMap<>();
1409 message.put("type", "didGainFocus");
1410 message.put("nodeId", 0);
1411 verify(mockChannel).send(message);
1412 }
1413
1414 @Test
1416 BasicMessageChannel mockChannel = mock(BasicMessageChannel.class);
1417 AccessibilityChannel accessibilityChannel =
1418 new AccessibilityChannel(mockChannel, mock(FlutterJNI.class));
1419 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1420 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1421 View mockRootView = mock(View.class);
1422 Context context = mock(Context.class);
1423 when(mockRootView.getContext()).thenReturn(context);
1424 when(context.getPackageName()).thenReturn("test");
1425 AccessibilityBridge accessibilityBridge =
1426 setUpBridge(
1427 /*rootAccessibilityView=*/ mockRootView,
1428 /*accessibilityChannel=*/ accessibilityChannel,
1429 /*accessibilityManager=*/ mockManager,
1430 /*contentResolver=*/ null,
1431 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1432 /*platformViewsAccessibilityDelegate=*/ null);
1433
1434 ViewParent mockParent = mock(ViewParent.class);
1435 when(mockRootView.getParent()).thenReturn(mockParent);
1436 when(mockManager.isEnabled()).thenReturn(true);
1437
1439 root.id = 0;
1440 root.label = "root";
1441
1442 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1443 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1444 // Set the focus on root.
1445 accessibilityBridge.performAction(0, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
1446 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1447 assertTrue(nodeInfo.isAccessibilityFocused());
1448 HashMap<String, Object> message = new HashMap<>();
1449 message.put("type", "didGainFocus");
1450 message.put("nodeId", 0);
1451 verify(mockChannel).send(message);
1452
1453 class Verifier {
1454 public Verifier(AccessibilityBridge accessibilityBridge) {
1455 this.accessibilityBridge = accessibilityBridge;
1456 }
1457
1458 public AccessibilityBridge accessibilityBridge;
1459 public boolean verified = false;
1460
1461 public boolean verify(InvocationOnMock invocation) {
1462 AccessibilityEvent event = (AccessibilityEvent) invocation.getArguments()[1];
1463 assertEquals(
1464 event.getEventType(), AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
1465 // The accessibility focus must be cleared before sending out
1466 // the TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED event.
1467 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0);
1468 assertFalse(nodeInfo.isAccessibilityFocused());
1469 verified = true;
1470 return true;
1471 }
1472 };
1473 Verifier verifier = new Verifier(accessibilityBridge);
1474 when(mockParent.requestSendAccessibilityEvent(eq(mockRootView), any(AccessibilityEvent.class)))
1475 .thenAnswer(invocation -> verifier.verify(invocation));
1476 accessibilityBridge.performAction(
1477 0, AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
1478 assertTrue(verifier.verified);
1479 }
1480
1481 @Test
1483 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1484 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1485 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1486 View mockRootView = mock(View.class);
1487 Context context = mock(Context.class);
1488 when(mockRootView.getContext()).thenReturn(context);
1489 when(context.getPackageName()).thenReturn("test");
1490 AccessibilityBridge accessibilityBridge =
1491 setUpBridge(
1492 /*rootAccessibilityView=*/ mockRootView,
1493 /*accessibilityChannel=*/ mockChannel,
1494 /*accessibilityManager=*/ mockManager,
1495 /*contentResolver=*/ null,
1496 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1497 /*platformViewsAccessibilityDelegate=*/ null);
1498
1499 ViewParent mockParent = mock(ViewParent.class);
1500 when(mockRootView.getParent()).thenReturn(mockParent);
1501 when(mockManager.isEnabled()).thenReturn(true);
1502
1504 root.id = 0;
1506 node1.id = 1;
1507 node1.value = "some text";
1508 node1.textSelectionBase = 0;
1509 node1.textSelectionExtent = 0;
1510 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
1511 root.children.add(node1);
1512 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1513 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1514 Bundle bundle = new Bundle();
1515 bundle.putInt(
1516 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1517 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
1518 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1519 accessibilityBridge.performAction(
1520 1, AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, bundle);
1521 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1522 // The seletction should be at the end of 'text'
1523 assertEquals(nodeInfo.getTextSelectionStart(), 9);
1524 assertEquals(nodeInfo.getTextSelectionEnd(), 9);
1525
1526 bundle = new Bundle();
1527 bundle.putInt(
1528 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1529 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
1530 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1531 accessibilityBridge.performAction(
1532 1, AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, bundle);
1533 nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1534 // The seletction should be go to beginning of 'text'.
1535 assertEquals(nodeInfo.getTextSelectionStart(), 5);
1536 assertEquals(nodeInfo.getTextSelectionEnd(), 5);
1537 }
1538
1539 @Test
1541 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1542 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1543 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1544 View mockRootView = mock(View.class);
1545 Context context = mock(Context.class);
1546 when(mockRootView.getContext()).thenReturn(context);
1547 when(context.getPackageName()).thenReturn("test");
1548 AccessibilityBridge accessibilityBridge =
1549 setUpBridge(
1550 /*rootAccessibilityView=*/ mockRootView,
1551 /*accessibilityChannel=*/ mockChannel,
1552 /*accessibilityManager=*/ mockManager,
1553 /*contentResolver=*/ null,
1554 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1555 /*platformViewsAccessibilityDelegate=*/ null);
1556
1557 ViewParent mockParent = mock(ViewParent.class);
1558 when(mockRootView.getParent()).thenReturn(mockParent);
1559 when(mockManager.isEnabled()).thenReturn(true);
1560
1562 root.id = 0;
1564 node1.id = 1;
1565 node1.value = "some text";
1566 node1.textSelectionBase = 0;
1567 node1.textSelectionExtent = 0;
1568 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
1569 root.children.add(node1);
1570 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1571 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1572 Bundle bundle = new Bundle();
1573 bundle.putInt(
1574 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1575 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
1576 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1577 accessibilityBridge.performAction(
1578 1, AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, bundle);
1579 ArgumentCaptor<AccessibilityEvent> eventCaptor =
1580 ArgumentCaptor.forClass(AccessibilityEvent.class);
1581 verify(mockParent, times(2))
1582 .requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
1583 AccessibilityEvent event = eventCaptor.getAllValues().get(1);
1584 assertEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
1585 assertEquals(event.getText().toString(), "[" + node1.value + "]");
1586 assertEquals(event.getFromIndex(), 1);
1587 assertEquals(event.getToIndex(), 1);
1588 assertEquals(event.getItemCount(), node1.value.length());
1589 }
1590
1591 @Test
1593 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1594 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1595 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1596 View mockRootView = mock(View.class);
1597 Context context = mock(Context.class);
1598 when(mockRootView.getContext()).thenReturn(context);
1599 when(context.getPackageName()).thenReturn("test");
1600 AccessibilityBridge accessibilityBridge =
1601 setUpBridge(
1602 /*rootAccessibilityView=*/ mockRootView,
1603 /*accessibilityChannel=*/ mockChannel,
1604 /*accessibilityManager=*/ mockManager,
1605 /*contentResolver=*/ null,
1606 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1607 /*platformViewsAccessibilityDelegate=*/ null);
1608
1609 ViewParent mockParent = mock(ViewParent.class);
1610 when(mockRootView.getParent()).thenReturn(mockParent);
1611 when(mockManager.isEnabled()).thenReturn(true);
1612
1614 root.id = 0;
1616 node1.id = 1;
1617 node1.value = "some text";
1618 node1.textSelectionBase = 0;
1619 node1.textSelectionExtent = 0;
1620 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
1621 root.children.add(node1);
1622 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1623 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1624 Bundle bundle = new Bundle();
1625 bundle.putInt(
1626 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1627 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
1628 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1629 accessibilityBridge.performAction(
1630 1, AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, bundle);
1631 ArgumentCaptor<AccessibilityEvent> eventCaptor =
1632 ArgumentCaptor.forClass(AccessibilityEvent.class);
1633 verify(mockParent, times(1))
1634 .requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
1635 assertEquals(eventCaptor.getAllValues().size(), 1);
1636 AccessibilityEvent event = eventCaptor.getAllValues().get(0);
1637 assertNotEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED);
1638 }
1639
1640 @Test
1642 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1643 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1644 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1645 View mockRootView = mock(View.class);
1646 Context context = mock(Context.class);
1647 when(mockRootView.getContext()).thenReturn(context);
1648 when(context.getPackageName()).thenReturn("test");
1649 AccessibilityBridge accessibilityBridge =
1650 setUpBridge(
1651 /*rootAccessibilityView=*/ mockRootView,
1652 /*accessibilityChannel=*/ mockChannel,
1653 /*accessibilityManager=*/ mockManager,
1654 /*contentResolver=*/ null,
1655 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1656 /*platformViewsAccessibilityDelegate=*/ null);
1657
1658 ViewParent mockParent = mock(ViewParent.class);
1659 when(mockRootView.getParent()).thenReturn(mockParent);
1660 when(mockManager.isEnabled()).thenReturn(true);
1661
1663 root.id = 0;
1665 node1.id = 1;
1666 node1.value = "你 好 嗎";
1667 node1.textSelectionBase = 0;
1668 node1.textSelectionExtent = 0;
1669 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
1670 root.children.add(node1);
1671 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1672 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1673 Bundle bundle = new Bundle();
1674 bundle.putInt(
1675 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1676 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
1677 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1678 accessibilityBridge.performAction(
1679 1, AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, bundle);
1680 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1681 // The seletction should be at the end of '好'
1682 assertEquals(nodeInfo.getTextSelectionStart(), 3);
1683 assertEquals(nodeInfo.getTextSelectionEnd(), 3);
1684
1685 bundle = new Bundle();
1686 bundle.putInt(
1687 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1688 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_WORD);
1689 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1690 accessibilityBridge.performAction(
1691 1, AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, bundle);
1692 nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1693 // The seletction should be go to beginning of '好'.
1694 assertEquals(nodeInfo.getTextSelectionStart(), 2);
1695 assertEquals(nodeInfo.getTextSelectionEnd(), 2);
1696 }
1697
1698 @Test
1700 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1701 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1702 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1703 View mockRootView = mock(View.class);
1704 Context context = mock(Context.class);
1705 when(mockRootView.getContext()).thenReturn(context);
1706 when(context.getPackageName()).thenReturn("test");
1707 AccessibilityBridge accessibilityBridge =
1708 setUpBridge(
1709 /*rootAccessibilityView=*/ mockRootView,
1710 /*accessibilityChannel=*/ mockChannel,
1711 /*accessibilityManager=*/ mockManager,
1712 /*contentResolver=*/ null,
1713 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1714 /*platformViewsAccessibilityDelegate=*/ null);
1715
1716 ViewParent mockParent = mock(ViewParent.class);
1717 when(mockRootView.getParent()).thenReturn(mockParent);
1718 when(mockManager.isEnabled()).thenReturn(true);
1719
1721 root.id = 0;
1723 node1.id = 1;
1724 node1.value = "How are you\nI am fine\nThank you";
1725 // Selection is at the second line.
1726 node1.textSelectionBase = 14;
1727 node1.textSelectionExtent = 14;
1728 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
1729 root.children.add(node1);
1730 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1731 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1732 Bundle bundle = new Bundle();
1733 bundle.putInt(
1734 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1735 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
1736 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1737 accessibilityBridge.performAction(
1738 1, AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, bundle);
1739 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1740 // The seletction should be at the beginning of the third line.
1741 assertEquals(nodeInfo.getTextSelectionStart(), 21);
1742 assertEquals(nodeInfo.getTextSelectionEnd(), 21);
1743
1744 bundle = new Bundle();
1745 bundle.putInt(
1746 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1747 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_LINE);
1748 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1749 accessibilityBridge.performAction(
1750 1, AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, bundle);
1751 nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1752 // The seletction should be at the beginning of the second line.
1753 assertEquals(nodeInfo.getTextSelectionStart(), 11);
1754 assertEquals(nodeInfo.getTextSelectionEnd(), 11);
1755 }
1756
1757 @Test
1759 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
1760 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1761 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1762 View mockRootView = mock(View.class);
1763 Context context = mock(Context.class);
1764 when(mockRootView.getContext()).thenReturn(context);
1765 when(context.getPackageName()).thenReturn("test");
1766 AccessibilityBridge accessibilityBridge =
1767 setUpBridge(
1768 /*rootAccessibilityView=*/ mockRootView,
1769 /*accessibilityChannel=*/ mockChannel,
1770 /*accessibilityManager=*/ mockManager,
1771 /*contentResolver=*/ null,
1772 /*accessibilityViewEmbedder=*/ mockViewEmbedder,
1773 /*platformViewsAccessibilityDelegate=*/ null);
1774
1775 ViewParent mockParent = mock(ViewParent.class);
1776 when(mockRootView.getParent()).thenReturn(mockParent);
1777 when(mockManager.isEnabled()).thenReturn(true);
1778
1780 root.id = 0;
1782 node1.id = 1;
1783 node1.value = "some text";
1784 node1.textSelectionBase = 0;
1785 node1.textSelectionExtent = 0;
1786 node1.addFlag(AccessibilityBridge.Flag.IS_TEXT_FIELD);
1787 root.children.add(node1);
1788 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1789 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1790 Bundle bundle = new Bundle();
1791 bundle.putInt(
1792 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1793 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
1794 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1795 accessibilityBridge.performAction(
1796 1, AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, bundle);
1797 AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1798 assertEquals(nodeInfo.getTextSelectionStart(), 1);
1799 assertEquals(nodeInfo.getTextSelectionEnd(), 1);
1800
1801 bundle = new Bundle();
1802 bundle.putInt(
1803 AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
1804 AccessibilityNodeInfo.MOVEMENT_GRANULARITY_CHARACTER);
1805 bundle.putBoolean(AccessibilityNodeInfo.ACTION_ARGUMENT_EXTEND_SELECTION_BOOLEAN, false);
1806 accessibilityBridge.performAction(
1807 1, AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY, bundle);
1808 nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(1);
1809 assertEquals(nodeInfo.getTextSelectionStart(), 0);
1810 assertEquals(nodeInfo.getTextSelectionEnd(), 0);
1811 }
1812
1813 @Test
1815 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1816 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1817 View mockRootView = mock(View.class);
1818 Context context = mock(Context.class);
1819 when(mockRootView.getContext()).thenReturn(context);
1820 when(context.getPackageName()).thenReturn("test");
1821 AccessibilityBridge accessibilityBridge =
1822 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
1823 ViewParent mockParent = mock(ViewParent.class);
1824 when(mockRootView.getParent()).thenReturn(mockParent);
1825 when(mockManager.isEnabled()).thenReturn(true);
1826
1827 // Sent a11y tree with scopeRoute without namesRoute.
1829 root.id = 0;
1830 TestSemanticsNode scopeRoute = new TestSemanticsNode();
1831 scopeRoute.id = 1;
1832 scopeRoute.addFlag(AccessibilityBridge.Flag.SCOPES_ROUTE);
1833 root.children.add(scopeRoute);
1834 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1835 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1836
1837 verify(mockRootView, times(1)).setAccessibilityPaneTitle(eq(" "));
1838 }
1839
1840 @Test
1842 // SementicsNode.hitTest() returns null when out of bounds.
1843 AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class);
1844 AccessibilityManager mockManager = mock(AccessibilityManager.class);
1845 View mockRootView = mock(View.class);
1846 Context context = mock(Context.class);
1847 when(mockRootView.getContext()).thenReturn(context);
1848 when(context.getPackageName()).thenReturn("test");
1849 AccessibilityBridge accessibilityBridge =
1850 setUpBridge(mockRootView, mockManager, mockViewEmbedder);
1851
1852 // Sent a11y tree with platform view.
1854 root.id = 0;
1855 TestSemanticsNode platformView = new TestSemanticsNode();
1856 platformView.id = 1;
1857 platformView.platformViewId = 42;
1858 root.children.add(platformView);
1859 TestSemanticsUpdate testSemanticsUpdate = root.toUpdate();
1860 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
1861
1862 // Pass an out of bounds MotionEvent.
1863 accessibilityBridge.onAccessibilityHoverEvent(MotionEvent.obtain(1, 1, 1, -10, -10, 0));
1864 }
1865
1866 @Test
1868 PlatformViewsAccessibilityDelegate accessibilityDelegate =
1869 mock(PlatformViewsAccessibilityDelegate.class);
1870
1871 Context context = ApplicationProvider.getApplicationContext();
1872 View rootAccessibilityView = new View(context);
1873 AccessibilityViewEmbedder accessibilityViewEmbedder = mock(AccessibilityViewEmbedder.class);
1874 AccessibilityBridge accessibilityBridge =
1875 setUpBridge(
1876 rootAccessibilityView,
1877 /*accessibilityChannel=*/ null,
1878 /*accessibilityManager=*/ null,
1879 /*contentResolver=*/ null,
1880 accessibilityViewEmbedder,
1881 accessibilityDelegate);
1882
1884 root.id = 0;
1885
1886 TestSemanticsNode platformView = new TestSemanticsNode();
1887 platformView.id = 1;
1888 platformView.platformViewId = 1;
1889 root.addChild(platformView);
1890
1891 TestSemanticsUpdate testSemanticsRootUpdate = root.toUpdate();
1892 testSemanticsRootUpdate.sendUpdateToBridge(accessibilityBridge);
1893
1894 TestSemanticsUpdate testSemanticsPlatformViewUpdate = platformView.toUpdate();
1895 testSemanticsPlatformViewUpdate.sendUpdateToBridge(accessibilityBridge);
1896
1897 View embeddedView = mock(View.class);
1898 when(accessibilityDelegate.getPlatformViewById(1)).thenReturn(embeddedView);
1899 when(accessibilityDelegate.usesVirtualDisplay(1)).thenReturn(false);
1900
1901 AccessibilityNodeInfo nodeInfo = mock(AccessibilityNodeInfo.class);
1902 when(embeddedView.createAccessibilityNodeInfo()).thenReturn(nodeInfo);
1903
1904 AccessibilityNodeInfo result = accessibilityBridge.createAccessibilityNodeInfo(0);
1905 assertNotNull(result);
1906 assertEquals(result.getChildCount(), 1);
1907 assertEquals(result.getClassName(), "android.view.View");
1908 }
1909
1910 @Test
1912 PlatformViewsAccessibilityDelegate accessibilityDelegate =
1913 mock(PlatformViewsAccessibilityDelegate.class);
1914
1915 Context context = ApplicationProvider.getApplicationContext();
1916 View rootAccessibilityView = new View(context);
1917 AccessibilityViewEmbedder accessibilityViewEmbedder = mock(AccessibilityViewEmbedder.class);
1918 AccessibilityBridge accessibilityBridge =
1919 setUpBridge(
1920 rootAccessibilityView,
1921 /*accessibilityChannel=*/ null,
1922 /*accessibilityManager=*/ null,
1923 /*contentResolver=*/ null,
1924 accessibilityViewEmbedder,
1925 accessibilityDelegate);
1926
1928 root.id = 0;
1929
1930 TestSemanticsNode platformView = new TestSemanticsNode();
1931 platformView.id = 1;
1932 platformView.platformViewId = 1;
1933 root.addChild(platformView);
1934
1935 View embeddedView = mock(View.class);
1936 when(accessibilityDelegate.getPlatformViewById(1)).thenReturn(embeddedView);
1937 when(accessibilityDelegate.usesVirtualDisplay(1)).thenReturn(false);
1938
1939 TestSemanticsUpdate testSemanticsRootUpdate = root.toUpdate();
1940 testSemanticsRootUpdate.sendUpdateToBridge(accessibilityBridge);
1941
1942 verify(embeddedView).setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_AUTO);
1943 }
1944
1945 @Test
1947 PlatformViewsAccessibilityDelegate accessibilityDelegate =
1948 mock(PlatformViewsAccessibilityDelegate.class);
1949
1950 Context context = ApplicationProvider.getApplicationContext();
1951 View rootAccessibilityView = new View(context);
1952 AccessibilityViewEmbedder accessibilityViewEmbedder = mock(AccessibilityViewEmbedder.class);
1953 AccessibilityBridge accessibilityBridge =
1954 setUpBridge(
1955 rootAccessibilityView,
1956 /*accessibilityChannel=*/ null,
1957 /*accessibilityManager=*/ null,
1958 /*contentResolver=*/ null,
1959 accessibilityViewEmbedder,
1960 accessibilityDelegate);
1961
1962 TestSemanticsNode rootWithPlatformView = new TestSemanticsNode();
1963 rootWithPlatformView.id = 0;
1964
1965 TestSemanticsNode platformView = new TestSemanticsNode();
1966 platformView.id = 1;
1967 platformView.platformViewId = 1;
1968 rootWithPlatformView.addChild(platformView);
1969
1970 View embeddedView = mock(View.class);
1971 when(accessibilityDelegate.getPlatformViewById(1)).thenReturn(embeddedView);
1972 when(accessibilityDelegate.usesVirtualDisplay(1)).thenReturn(false);
1973
1974 TestSemanticsUpdate testSemanticsRootWithPlatformViewUpdate = rootWithPlatformView.toUpdate();
1975 testSemanticsRootWithPlatformViewUpdate.sendUpdateToBridge(accessibilityBridge);
1976
1977 TestSemanticsNode rootWithoutPlatformView = new TestSemanticsNode();
1978 rootWithoutPlatformView.id = 0;
1979 TestSemanticsUpdate testSemanticsRootWithoutPlatformViewUpdate =
1980 rootWithoutPlatformView.toUpdate();
1981 testSemanticsRootWithoutPlatformViewUpdate.sendUpdateToBridge(accessibilityBridge);
1982
1983 verify(embeddedView)
1984 .setImportantForAccessibility(View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
1985 }
1986
1987 @Test
1989 PlatformViewsAccessibilityDelegate accessibilityDelegate =
1990 mock(PlatformViewsAccessibilityDelegate.class);
1991 AccessibilityViewEmbedder accessibilityViewEmbedder = mock(AccessibilityViewEmbedder.class);
1992 AccessibilityBridge accessibilityBridge =
1993 setUpBridge(
1994 /*rootAccessibilityView=*/ null,
1995 /*accessibilityChannel=*/ null,
1996 /*accessibilityManager=*/ null,
1997 /*contentResolver=*/ null,
1998 accessibilityViewEmbedder,
1999 accessibilityDelegate);
2000
2001 TestSemanticsNode platformView = new TestSemanticsNode();
2002 platformView.platformViewId = 1;
2003
2004 TestSemanticsUpdate testSemanticsUpdate = platformView.toUpdate();
2005 testSemanticsUpdate.sendUpdateToBridge(accessibilityBridge);
2006
2007 View embeddedView = mock(View.class);
2008 when(accessibilityDelegate.getPlatformViewById(1)).thenReturn(embeddedView);
2009 when(accessibilityDelegate.usesVirtualDisplay(1)).thenReturn(true);
2010
2011 accessibilityBridge.createAccessibilityNodeInfo(0);
2012 verify(accessibilityViewEmbedder).getRootNode(eq(embeddedView), eq(0), any(Rect.class));
2013 }
2014
2015 @Test
2017 AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
2018 AccessibilityManager mockManager = mock(AccessibilityManager.class);
2019 ContentResolver mockContentResolver = mock(ContentResolver.class);
2020 when(mockManager.isEnabled()).thenReturn(true);
2021 AccessibilityBridge accessibilityBridge =
2022 setUpBridge(null, mockChannel, mockManager, mockContentResolver, null, null);
2023 verify(mockChannel)
2024 .setAccessibilityMessageHandler(
2025 any(AccessibilityChannel.AccessibilityMessageHandler.class));
2026 ArgumentCaptor<AccessibilityManager.AccessibilityStateChangeListener> stateListenerCaptor =
2027 ArgumentCaptor.forClass(AccessibilityManager.AccessibilityStateChangeListener.class);
2028 ArgumentCaptor<AccessibilityManager.TouchExplorationStateChangeListener> touchListenerCaptor =
2029 ArgumentCaptor.forClass(AccessibilityManager.TouchExplorationStateChangeListener.class);
2030 verify(mockManager).addAccessibilityStateChangeListener(stateListenerCaptor.capture());
2031 verify(mockManager).addTouchExplorationStateChangeListener(touchListenerCaptor.capture());
2032 accessibilityBridge.release();
2033 verify(mockChannel).setAccessibilityMessageHandler(null);
2034 reset(mockChannel);
2035 stateListenerCaptor.getValue().onAccessibilityStateChanged(true);
2036 verify(mockChannel, never()).onAndroidAccessibilityEnabled();
2037 touchListenerCaptor.getValue().onTouchExplorationStateChanged(true);
2038 verify(mockChannel, never()).setAccessibilityFeatures(anyInt());
2039 }
2040
2041 @Test
2043 AccessibilityManager mockManager = mock(AccessibilityManager.class);
2044 AccessibilityChannel accessibilityChannel =
2045 new AccessibilityChannel(mock(DartExecutor.class), mock(FlutterJNI.class));
2046
2047 ContentResolver mockContentResolver = mock(ContentResolver.class);
2048 View mockRootView = mock(View.class);
2049 Context context = mock(Context.class);
2050 when(mockRootView.getContext()).thenReturn(context);
2051 when(context.getPackageName()).thenReturn("test");
2052 ViewParent mockParent = mock(ViewParent.class);
2053 when(mockRootView.getParent()).thenReturn(mockParent);
2054 when(mockManager.isEnabled()).thenReturn(true);
2055
2056 AccessibilityBridge accessibilityBridge =
2057 setUpBridge(mockRootView, accessibilityChannel, mockManager, null, null, null);
2058
2059 HashMap<String, Object> arguments = new HashMap<>();
2060 arguments.put("type", "focus");
2061 arguments.put("nodeId", 123);
2062 BasicMessageChannel.Reply reply = mock(BasicMessageChannel.Reply.class);
2063 accessibilityChannel.parsingMessageHandler.onMessage(arguments, reply);
2064
2065 // Check that focus event was sent.
2066 ArgumentCaptor<AccessibilityEvent> eventCaptor =
2067 ArgumentCaptor.forClass(AccessibilityEvent.class);
2068 verify(mockParent).requestSendAccessibilityEvent(eq(mockRootView), eventCaptor.capture());
2069 AccessibilityEvent event = eventCaptor.getAllValues().get(0);
2070 assertEquals(event.getEventType(), AccessibilityEvent.TYPE_VIEW_FOCUSED);
2071 assertEquals(event.getSource(), null);
2072 }
2073
2074 @Test
2076 AccessibilityManager mockManager = mock(AccessibilityManager.class);
2077 ContentResolver mockContentResolver = mock(ContentResolver.class);
2078 View mockRootView = mock(View.class);
2079 Context context = mock(Context.class);
2080 when(mockRootView.getContext()).thenReturn(context);
2081 when(context.getPackageName()).thenReturn("test");
2082 when(mockManager.isEnabled()).thenReturn(true);
2083 ViewParent mockParent = mock(ViewParent.class);
2084 when(mockRootView.getParent()).thenReturn(mockParent);
2085 AccessibilityEvent mockEvent = mock(AccessibilityEvent.class);
2086
2087 AccessibilityBridge accessibilityBridge =
2088 setUpBridge(mockRootView, null, mockManager, null, null, null);
2089
2090 AccessibilityBridge spyAccessibilityBridge = spy(accessibilityBridge);
2091
2092 when(spyAccessibilityBridge.obtainAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED))
2093 .thenReturn(mockEvent);
2094
2095 spyAccessibilityBridge.sendAccessibilityEvent(123, AccessibilityEvent.TYPE_VIEW_FOCUSED);
2096
2097 verify(mockEvent).setPackageName("test");
2098 verify(mockEvent).setSource(eq(mockRootView), eq(123));
2099 }
2100
2102 return setUpBridge(null, null, null, null, null, null);
2103 }
2104
2106 View rootAccessibilityView,
2107 AccessibilityManager accessibilityManager,
2108 AccessibilityViewEmbedder accessibilityViewEmbedder) {
2109 return setUpBridge(
2110 rootAccessibilityView, null, accessibilityManager, null, accessibilityViewEmbedder, null);
2111 }
2112
2114 View rootAccessibilityView,
2115 AccessibilityChannel accessibilityChannel,
2116 AccessibilityManager accessibilityManager,
2117 ContentResolver contentResolver,
2118 AccessibilityViewEmbedder accessibilityViewEmbedder,
2119 PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate) {
2120 if (rootAccessibilityView == null) {
2121 rootAccessibilityView = mock(View.class);
2122 Context context = mock(Context.class);
2123 when(rootAccessibilityView.getContext()).thenReturn(context);
2124 when(context.getPackageName()).thenReturn("test");
2125 }
2126 if (accessibilityChannel == null) {
2127 accessibilityChannel = mock(AccessibilityChannel.class);
2128 }
2129 if (accessibilityManager == null) {
2130 accessibilityManager = mock(AccessibilityManager.class);
2131 }
2132 if (contentResolver == null) {
2133 contentResolver = mock(ContentResolver.class);
2134 }
2135 if (accessibilityViewEmbedder == null) {
2136 accessibilityViewEmbedder = mock(AccessibilityViewEmbedder.class);
2137 }
2138 if (platformViewsAccessibilityDelegate == null) {
2139 platformViewsAccessibilityDelegate = mock(PlatformViewsAccessibilityDelegate.class);
2140 }
2141 return new AccessibilityBridge(
2142 rootAccessibilityView,
2143 accessibilityChannel,
2144 accessibilityManager,
2145 contentResolver,
2146 accessibilityViewEmbedder,
2147 platformViewsAccessibilityDelegate);
2148 }
2149
2150 /// The encoding for semantics is described in platform_view_android.cc
2152 TestSemanticsUpdate(ByteBuffer buffer, String[] strings, ByteBuffer[] stringAttributeArgs) {
2153 this.buffer = buffer;
2154 this.strings = strings;
2155 this.stringAttributeArgs = stringAttributeArgs;
2156 }
2157
2159 bridge.updateSemantics(buffer, strings, stringAttributeArgs);
2160 }
2161
2162 final ByteBuffer buffer;
2163 final String[] strings;
2164 final ByteBuffer[] stringAttributeArgs;
2165 }
2166
2170
2171 private final int value;
2172
2173 private TestStringAttributeType(int value) {
2174 this.value = value;
2175 }
2176
2177 public int getValue() {
2178 return value;
2179 }
2180 }
2181
2187
2189
2193
2196
2198 flags |= flag.value;
2199 }
2200
2202 actions |= action.value;
2203 }
2204
2205 // These fields are declared in the order they should be
2206 // encoded.
2207 int id = 0;
2208 int flags = 0;
2209 int actions = 0;
2210 int maxValueLength = 0;
2211 int currentValueLength = 0;
2212 int textSelectionBase = 0;
2213 int textSelectionExtent = 0;
2214 int platformViewId = -1;
2215 int scrollChildren = 0;
2216 int scrollIndex = 0;
2217 float scrollPosition = 0.0f;
2218 float scrollExtentMax = 0.0f;
2219 float scrollExtentMin = 0.0f;
2220 String identifier = null;
2221 String label = null;
2223 String value = null;
2225 String increasedValue = null;
2227 String decreasedValue = null;
2229 String hint = null;
2231 String tooltip = null;
2232 int textDirection = 0;
2233 float left = 0.0f;
2234 float top = 0.0f;
2235 float right = 0.0f;
2236 float bottom = 0.0f;
2237 float[] transform =
2238 new float[] {
2239 1.0f, 0.0f, 0.0f, 0.0f,
2240 0.0f, 1.0f, 0.0f, 0.0f,
2241 0.0f, 0.0f, 1.0f, 0.0f,
2242 0.0f, 0.0f, 0.0f, 1.0f
2243 };
2244 final List<TestSemanticsNode> children = new ArrayList<TestSemanticsNode>();
2245
2246 public void addChild(TestSemanticsNode child) {
2247 children.add(child);
2248 }
2249 // custom actions not supported.
2250
2252 ArrayList<String> strings = new ArrayList<String>();
2253 ByteBuffer bytes = ByteBuffer.allocate(1000);
2254 ArrayList<ByteBuffer> stringAttributeArgs = new ArrayList<ByteBuffer>();
2255 addToBuffer(bytes, strings, stringAttributeArgs);
2256 bytes.flip();
2257 return new TestSemanticsUpdate(
2258 bytes,
2259 strings.toArray(new String[strings.size()]),
2260 stringAttributeArgs.toArray(new ByteBuffer[stringAttributeArgs.size()]));
2261 }
2262
2263 protected void addToBuffer(
2264 ByteBuffer bytes, ArrayList<String> strings, ArrayList<ByteBuffer> stringAttributeArgs) {
2265 bytes.putInt(id);
2266 bytes.putInt(flags);
2267 bytes.putInt(actions);
2268 bytes.putInt(maxValueLength);
2269 bytes.putInt(currentValueLength);
2270 bytes.putInt(textSelectionBase);
2271 bytes.putInt(textSelectionExtent);
2272 bytes.putInt(platformViewId);
2273 bytes.putInt(scrollChildren);
2274 bytes.putInt(scrollIndex);
2275 bytes.putFloat(scrollPosition);
2276 bytes.putFloat(scrollExtentMax);
2277 bytes.putFloat(scrollExtentMin);
2278 if (identifier == null) {
2279 bytes.putInt(-1);
2280 } else {
2281 strings.add(identifier);
2282 bytes.putInt(strings.size() - 1);
2283 }
2284 updateString(label, labelAttributes, bytes, strings, stringAttributeArgs);
2285 updateString(value, valueAttributes, bytes, strings, stringAttributeArgs);
2286 updateString(increasedValue, increasedValueAttributes, bytes, strings, stringAttributeArgs);
2287 updateString(decreasedValue, decreasedValueAttributes, bytes, strings, stringAttributeArgs);
2288 updateString(hint, hintAttributes, bytes, strings, stringAttributeArgs);
2289 if (tooltip == null) {
2290 bytes.putInt(-1);
2291 } else {
2292 strings.add(tooltip);
2293 bytes.putInt(strings.size() - 1);
2294 }
2295 bytes.putInt(textDirection);
2296 bytes.putFloat(left);
2297 bytes.putFloat(top);
2298 bytes.putFloat(right);
2299 bytes.putFloat(bottom);
2300 // transform.
2301 for (int i = 0; i < 16; i++) {
2302 bytes.putFloat(transform[i]);
2303 }
2304 // children in traversal order.
2305 bytes.putInt(children.size());
2306 for (TestSemanticsNode node : children) {
2307 bytes.putInt(node.id);
2308 }
2309 // children in hit test order.
2310 for (TestSemanticsNode node : children) {
2311 bytes.putInt(node.id);
2312 }
2313 // custom actions
2314 bytes.putInt(0);
2315 // child nodes
2316 for (TestSemanticsNode node : children) {
2317 node.addToBuffer(bytes, strings, stringAttributeArgs);
2318 }
2319 }
2320 }
2321
2322 static void updateString(
2323 String value,
2324 List<TestStringAttribute> attributes,
2325 ByteBuffer bytes,
2326 ArrayList<String> strings,
2327 ArrayList<ByteBuffer> stringAttributeArgs) {
2328 if (value == null) {
2329 bytes.putInt(-1);
2330 } else {
2331 strings.add(value);
2332 bytes.putInt(strings.size() - 1);
2333 }
2334 // attributes
2335 if (attributes == null || attributes.isEmpty()) {
2336 bytes.putInt(-1);
2337 return;
2338 }
2339 bytes.putInt(attributes.size());
2340 for (TestStringAttribute attribute : attributes) {
2341 bytes.putInt(attribute.start);
2342 bytes.putInt(attribute.end);
2343 bytes.putInt(attribute.type.getValue());
2344 switch (attribute.type) {
2345 case SPELLOUT:
2346 bytes.putInt(-1);
2347 break;
2348 case LOCALE:
2349 bytes.putInt(stringAttributeArgs.size());
2350 TestStringAttributeLocale localeAttribute = (TestStringAttributeLocale) attribute;
2351 stringAttributeArgs.add(Charset.forName("UTF-8").encode(localeAttribute.locale));
2352 break;
2353 }
2354 }
2355 }
2356}
static SkISize times(const SkISize &size, float factor)
m reset()
static bool eq(const SkM44 &a, const SkM44 &b, float tol)
Definition M44Test.cpp:18
static bool left(const SkPoint &p0, const SkPoint &p1)
static bool right(const SkPoint &p0, const SkPoint &p1)
void add(sk_sp< SkIDChangeListener > listener) SK_EXCLUDES(fMutex)
void addToBuffer(ByteBuffer bytes, ArrayList< String > strings, ArrayList< ByteBuffer > stringAttributeArgs)
The encoding for semantics is described in platform_view_android.cc.
TestSemanticsUpdate(ByteBuffer buffer, String[] strings, ByteBuffer[] stringAttributeArgs)
AccessibilityBridge setUpBridge(View rootAccessibilityView, AccessibilityManager accessibilityManager, AccessibilityViewEmbedder accessibilityViewEmbedder)
static void updateString(String value, List< TestStringAttribute > attributes, ByteBuffer bytes, ArrayList< String > strings, ArrayList< ByteBuffer > stringAttributeArgs)
AccessibilityBridge setUpBridge(View rootAccessibilityView, AccessibilityChannel accessibilityChannel, AccessibilityManager accessibilityManager, ContentResolver contentResolver, AccessibilityViewEmbedder accessibilityViewEmbedder, PlatformViewsAccessibilityDelegate platformViewsAccessibilityDelegate)
Integer getRecordFlutterId( @NonNull View embeddedView, @NonNull AccessibilityRecord record)
boolean requestSendAccessibilityEvent( @NonNull View embeddedView, @NonNull View eventOrigin, @NonNull AccessibilityEvent event)
static SkString identifier(const FontFamilyDesc &family, const FontDesc &font)
GLFWwindow * window
Definition main.cc:45
FlutterSemanticsFlag flag
FlutterSemanticsFlag flags
FlKeyEvent * event
static const uint8_t buffer[]
uint8_t value
GAsyncResult * result
Win32Message message
static SkColor4f transform(SkColor4f c, SkColorSpace *src, SkColorSpace *dst)
Definition p3.cpp:47