From bddb13004d55b71b6c6f2ba355d05ba7bd353a67 Mon Sep 17 00:00:00 2001 From: eggfly Date: Wed, 21 Apr 2021 00:37:25 +0800 Subject: [PATCH] Fix: add Android accessibility bounds offset when FlutterView's location is not left top corner (flutter/engine#25670) --- .../io/flutter/view/AccessibilityBridge.java | 17 ++++++++- .../flutter/view/AccessibilityBridgeTest.java | 36 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java b/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java index 9616d444ecb..7ac46d7a147 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/view/AccessibilityBridge.java @@ -693,7 +693,8 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { } else { result.setBoundsInParent(bounds); } - result.setBoundsInScreen(bounds); + final Rect boundsInScreen = getBoundsInScreen(bounds); + result.setBoundsInScreen(boundsInScreen); result.setVisibleToUser(true); result.setEnabled( !semanticsNode.hasFlag(Flag.HAS_ENABLED_STATE) || semanticsNode.hasFlag(Flag.IS_ENABLED)); @@ -869,6 +870,20 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { return result; } + /** + * Get the bounds in screen with root FlutterView's offset. + * + * @param bounds the bounds in FlutterView + * @return the bounds with offset + */ + private Rect getBoundsInScreen(Rect bounds) { + Rect boundsInScreen = new Rect(bounds); + int[] locationOnScreen = new int[2]; + rootAccessibilityView.getLocationOnScreen(locationOnScreen); + boundsInScreen.offset(locationOnScreen[0], locationOnScreen[1]); + return boundsInScreen; + } + /** * Instructs the view represented by {@code virtualViewId} to carry out the desired {@code * accessibilityAction}, perhaps configured by additional {@code arguments}. diff --git a/engine/src/flutter/shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java b/engine/src/flutter/shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java index ca9c01d2c15..3c50399c760 100644 --- a/engine/src/flutter/shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java +++ b/engine/src/flutter/shell/platform/android/test/io/flutter/view/AccessibilityBridgeTest.java @@ -10,6 +10,7 @@ import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; @@ -76,6 +77,41 @@ public class AccessibilityBridgeTest { assertEquals(nodeInfo.getText(), "Hello, World"); } + @Test + public void itTakesGlobalCoordinatesOfFlutterViewIntoAccount() { + AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class); + AccessibilityManager mockManager = mock(AccessibilityManager.class); + View mockRootView = mock(View.class); + Context context = mock(Context.class); + when(mockRootView.getContext()).thenReturn(context); + final int position = 88; + // The getBoundsInScreen() in createAccessibilityNodeInfo() needs View.getLocationOnScreen() + doAnswer( + invocation -> { + int[] outLocation = (int[]) invocation.getArguments()[0]; + outLocation[0] = position; + outLocation[1] = position; + return null; + }) + .when(mockRootView) + .getLocationOnScreen(any(int[].class)); + + when(context.getPackageName()).thenReturn("test"); + AccessibilityBridge accessibilityBridge = + setUpBridge(mockRootView, mockManager, mockViewEmbedder); + + TestSemanticsNode testSemanticsNode = new TestSemanticsNode(); + TestSemanticsUpdate testSemanticsUpdate = testSemanticsNode.toUpdate(); + + accessibilityBridge.updateSemantics(testSemanticsUpdate.buffer, testSemanticsUpdate.strings); + AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0); + + Rect outBoundsInScreen = new Rect(); + nodeInfo.getBoundsInScreen(outBoundsInScreen); + assertEquals(position, outBoundsInScreen.left); + assertEquals(position, outBoundsInScreen.top); + } + @Test public void itDoesNotContainADescriptionIfScopesRoute() { AccessibilityBridge accessibilityBridge = setUpBridge();