未验证 提交 fc4da62b 编写于 作者: J Jason Simmons 提交者: GitHub

Ignore calls to AccessibilityBridge listeners after release (#20701)

AccessibilityBridge installs various listeners for Android events
that invoke Flutter engine APIs.  These listeners are removed in
AccessibilityBridge.release.  However, in some environments there may
be deferred calls to the listener that will still execute even after
the listener has been removed.  This change sets a flag during release
and ignores any listener invocations that happen after the flag is set.

See https://github.com/flutter/flutter/issues/63555 and
https://github.com/flutter/engine/pull/17311
上级 f0a8230f
......@@ -223,6 +223,9 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
@Nullable private OnAccessibilityChangeListener onAccessibilityChangeListener;
// Set to true after {@code release} has been invoked.
private boolean isReleased = false;
// Handler for all messages received from Flutter via the {@code accessibilityChannel}
private final AccessibilityChannel.AccessibilityMessageHandler accessibilityMessageHandler =
new AccessibilityChannel.AccessibilityMessageHandler() {
......@@ -274,6 +277,9 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
new AccessibilityManager.AccessibilityStateChangeListener() {
@Override
public void onAccessibilityStateChanged(boolean accessibilityEnabled) {
if (isReleased) {
return;
}
if (accessibilityEnabled) {
accessibilityChannel.setAccessibilityMessageHandler(accessibilityMessageHandler);
accessibilityChannel.onAndroidAccessibilityEnabled();
......@@ -307,6 +313,9 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
@Override
public void onChange(boolean selfChange, Uri uri) {
if (isReleased) {
return;
}
// Retrieve the current value of TRANSITION_ANIMATION_SCALE from the OS.
String value =
Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1
......@@ -374,6 +383,9 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
new AccessibilityManager.TouchExplorationStateChangeListener() {
@Override
public void onTouchExplorationStateChanged(boolean isTouchExplorationEnabled) {
if (isReleased) {
return;
}
if (isTouchExplorationEnabled) {
accessibilityFeatureFlags |= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value;
} else {
......@@ -421,6 +433,7 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
* on this {@code AccessibilityBridge} after invoking {@code release()} is undefined.
*/
public void release() {
isReleased = true;
// platformViewsAccessibilityDelegate should be @NonNull once the plumbing
// for io.flutter.embedding.engine.android.FlutterView is done.
// TODO(mattcarrol): Remove the null check once the plumbing is done.
......
......@@ -7,11 +7,15 @@ package io.flutter.view;
import static org.junit.Assert.assertEquals;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.annotation.TargetApi;
import android.content.ContentResolver;
import android.content.Context;
import android.view.MotionEvent;
......@@ -33,6 +37,7 @@ import org.robolectric.annotation.Config;
@Config(manifest = Config.NONE)
@RunWith(RobolectricTestRunner.class)
@TargetApi(19)
public class AccessibilityBridgeTest {
@Test
......@@ -197,14 +202,26 @@ public class AccessibilityBridgeTest {
public void releaseDropsChannelMessageHandler() {
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
AccessibilityManager mockManager = mock(AccessibilityManager.class);
ContentResolver mockContentResolver = mock(ContentResolver.class);
when(mockManager.isEnabled()).thenReturn(true);
AccessibilityBridge accessibilityBridge =
setUpBridge(null, mockChannel, mockManager, null, null, null);
setUpBridge(null, mockChannel, mockManager, mockContentResolver, null, null);
verify(mockChannel)
.setAccessibilityMessageHandler(
any(AccessibilityChannel.AccessibilityMessageHandler.class));
ArgumentCaptor<AccessibilityManager.AccessibilityStateChangeListener> stateListenerCaptor =
ArgumentCaptor.forClass(AccessibilityManager.AccessibilityStateChangeListener.class);
ArgumentCaptor<AccessibilityManager.TouchExplorationStateChangeListener> touchListenerCaptor =
ArgumentCaptor.forClass(AccessibilityManager.TouchExplorationStateChangeListener.class);
verify(mockManager).addAccessibilityStateChangeListener(stateListenerCaptor.capture());
verify(mockManager).addTouchExplorationStateChangeListener(touchListenerCaptor.capture());
accessibilityBridge.release();
verify(mockChannel).setAccessibilityMessageHandler(null);
reset(mockChannel);
stateListenerCaptor.getValue().onAccessibilityStateChanged(true);
verify(mockChannel, never()).onAndroidAccessibilityEnabled();
touchListenerCaptor.getValue().onTouchExplorationStateChanged(true);
verify(mockChannel, never()).setAccessibilityFeatures(anyInt());
}
AccessibilityBridge setUpBridge() {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册