From cd4c07d7a2bdf5cbb58242ab1cdf5fae6aead235 Mon Sep 17 00:00:00 2001 From: hangyu Date: Fri, 17 Nov 2023 13:31:33 -0800 Subject: [PATCH] [Android] Send " did gain focus" message from engine to framework (flutter/engine#47114) issue:https://github.com/flutter/flutter/issues/97747 framework pr:https://github.com/flutter/flutter/pull/135771 ## Pre-launch Checklist - [ ] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [ ] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [ ] I read and followed the [Flutter Style Guide] and the [C++, Objective-C, Java style guides]. - [ ] I listed at least one issue that this PR fixes in the description above. - [ ] I added new tests to check the change I am making or feature I am adding, or the PR is [test-exempt]. See [testing the engine] for instructions on writing and running engine tests. - [ ] I updated/added relevant documentation (doc comments with `///`). - [ ] I signed the [CLA]. - [ ] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/wiki/Tree-hygiene#overview [Tree Hygiene]: https://github.com/flutter/flutter/wiki/Tree-hygiene [test-exempt]: https://github.com/flutter/flutter/wiki/Tree-hygiene#tests [Flutter Style Guide]: https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo [C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style [testing the engine]: https://github.com/flutter/flutter/wiki/Testing-the-engine [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/wiki/Tree-hygiene#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/wiki/Chat --------- Co-authored-by: Reid Baker --- .../systemchannels/AccessibilityChannel.java | 8 +++++ .../io/flutter/view/AccessibilityBridge.java | 6 ++++ .../flutter/view/AccessibilityBridgeTest.java | 33 +++++++++++++++---- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java index 38059232ce..f2d12b181a 100644 --- a/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java +++ b/engine/src/flutter/shell/platform/android/io/flutter/embedding/engine/systemchannels/AccessibilityChannel.java @@ -2,6 +2,7 @@ package io.flutter.embedding.engine.systemchannels; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import io.flutter.Log; import io.flutter.embedding.engine.FlutterJNI; import io.flutter.embedding.engine.dart.DartExecutor; @@ -102,6 +103,13 @@ public class AccessibilityChannel { this.flutterJNI = flutterJNI; } + @VisibleForTesting + public AccessibilityChannel( + @NonNull BasicMessageChannel channel, @NonNull FlutterJNI flutterJNI) { + this.channel = channel; + this.flutterJNI = flutterJNI; + } + /** * Informs Flutter that the Android OS currently has accessibility enabled. * 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 c7c249713f..8f2584cb74 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 @@ -1166,6 +1166,12 @@ public class AccessibilityBridge extends AccessibilityNodeProvider { accessibilityChannel.dispatchSemanticsAction( virtualViewId, Action.DID_GAIN_ACCESSIBILITY_FOCUS); + + HashMap message = new HashMap<>(); + message.put("type", "didGainFocus"); + message.put("nodeId", semanticsNode.id); + accessibilityChannel.channel.send(message); + sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED); if (semanticsNode.hasAction(Action.INCREASE) 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 28077a2c68..12054c9638 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 @@ -1187,7 +1187,9 @@ public class AccessibilityBridgeTest { @Test public void itPerformsClearAccessibilityFocusCorrectly() { - AccessibilityChannel mockChannel = mock(AccessibilityChannel.class); + BasicMessageChannel mockChannel = mock(BasicMessageChannel.class); + AccessibilityChannel accessibilityChannel = + new AccessibilityChannel(mockChannel, mock(FlutterJNI.class)); AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class); AccessibilityManager mockManager = mock(AccessibilityManager.class); View mockRootView = mock(View.class); @@ -1197,7 +1199,7 @@ public class AccessibilityBridgeTest { AccessibilityBridge accessibilityBridge = setUpBridge( /*rootAccessibilityView=*/ mockRootView, - /*accessibilityChannel=*/ mockChannel, + /*accessibilityChannel=*/ accessibilityChannel, /*accessibilityManager=*/ mockManager, /*contentResolver=*/ null, /*accessibilityViewEmbedder=*/ mockViewEmbedder, @@ -1220,6 +1222,11 @@ public class AccessibilityBridgeTest { accessibilityBridge.performAction(0, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0); assertTrue(nodeInfo.isAccessibilityFocused()); + + HashMap message = new HashMap<>(); + message.put("type", "didGainFocus"); + message.put("nodeId", 0); + verify(mockChannel).send(message); // Clear focus on non-focused node shouldn't do anything accessibilityBridge.performAction( 1, AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null); @@ -1310,7 +1317,10 @@ public class AccessibilityBridgeTest { @Test public void itSetsFocusedNodeBeforeSendingEvent() { - AccessibilityChannel mockChannel = mock(AccessibilityChannel.class); + BasicMessageChannel mockChannel = mock(BasicMessageChannel.class); + AccessibilityChannel accessibilityChannel = + new AccessibilityChannel(mockChannel, mock(FlutterJNI.class)); + AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class); AccessibilityManager mockManager = mock(AccessibilityManager.class); View mockRootView = mock(View.class); @@ -1320,7 +1330,7 @@ public class AccessibilityBridgeTest { AccessibilityBridge accessibilityBridge = setUpBridge( /*rootAccessibilityView=*/ mockRootView, - /*accessibilityChannel=*/ mockChannel, + /*accessibilityChannel=*/ accessibilityChannel, /*accessibilityManager=*/ mockManager, /*contentResolver=*/ null, /*accessibilityViewEmbedder=*/ mockViewEmbedder, @@ -1361,11 +1371,18 @@ public class AccessibilityBridgeTest { .thenAnswer(invocation -> verifier.verify(invocation)); accessibilityBridge.performAction(0, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); assertTrue(verifier.verified); + + HashMap message = new HashMap<>(); + message.put("type", "didGainFocus"); + message.put("nodeId", 0); + verify(mockChannel).send(message); } @Test public void itClearsFocusedNodeBeforeSendingEvent() { - AccessibilityChannel mockChannel = mock(AccessibilityChannel.class); + BasicMessageChannel mockChannel = mock(BasicMessageChannel.class); + AccessibilityChannel accessibilityChannel = + new AccessibilityChannel(mockChannel, mock(FlutterJNI.class)); AccessibilityViewEmbedder mockViewEmbedder = mock(AccessibilityViewEmbedder.class); AccessibilityManager mockManager = mock(AccessibilityManager.class); View mockRootView = mock(View.class); @@ -1375,7 +1392,7 @@ public class AccessibilityBridgeTest { AccessibilityBridge accessibilityBridge = setUpBridge( /*rootAccessibilityView=*/ mockRootView, - /*accessibilityChannel=*/ mockChannel, + /*accessibilityChannel=*/ accessibilityChannel, /*accessibilityManager=*/ mockManager, /*contentResolver=*/ null, /*accessibilityViewEmbedder=*/ mockViewEmbedder, @@ -1395,6 +1412,10 @@ public class AccessibilityBridgeTest { accessibilityBridge.performAction(0, AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null); AccessibilityNodeInfo nodeInfo = accessibilityBridge.createAccessibilityNodeInfo(0); assertTrue(nodeInfo.isAccessibilityFocused()); + HashMap message = new HashMap<>(); + message.put("type", "didGainFocus"); + message.put("nodeId", 0); + verify(mockChannel).send(message); class Verifier { public Verifier(AccessibilityBridge accessibilityBridge) {