Updates accessible_navigation trigger in Android (flutter/engine#35478)

* Updates accessible_navigation trigger in Android

* update doc comments

* comment

* addressing comment
This commit is contained in:
chunhtai 2022-08-19 13:54:47 -07:00 committed by GitHub
parent c01ef41e9e
commit 1a38d2e54b
2 changed files with 66 additions and 6 deletions

View File

@ -257,6 +257,30 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
@Nullable private OnAccessibilityChangeListener onAccessibilityChangeListener;
// Whether the users are using assistive technologies to interact with the devices.
//
// The getter returns true when at least one of the assistive technologies is running:
// TalkBack, SwitchAccess, or VoiceAccess.
@VisibleForTesting
public boolean getAccessibleNavigation() {
return accessibleNavigation;
}
private boolean accessibleNavigation = false;
private void setAccessibleNavigation(boolean value) {
if (accessibleNavigation == value) {
return;
}
accessibleNavigation = value;
if (accessibleNavigation) {
accessibilityFeatureFlags |= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value;
} else {
accessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value;
}
sendLatestAccessibilityFlagsToFlutter();
}
// Set to true after {@code release} has been invoked.
private boolean isReleased = false;
@ -331,6 +355,7 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
accessibilityChannel.setAccessibilityMessageHandler(accessibilityMessageHandler);
accessibilityChannel.onAndroidAccessibilityEnabled();
} else {
setAccessibleNavigation(false);
accessibilityChannel.setAccessibilityMessageHandler(null);
accessibilityChannel.onAndroidAccessibilityDisabled();
}
@ -409,7 +434,6 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
this.contentResolver = contentResolver;
this.accessibilityViewEmbedder = accessibilityViewEmbedder;
this.platformViewsAccessibilityDelegate = platformViewsAccessibilityDelegate;
// Tell Flutter whether accessibility is initially active or not. Then register a listener
// to be notified of changes in the future.
accessibilityStateChangeListener.onAccessibilityStateChanged(accessibilityManager.isEnabled());
@ -425,13 +449,10 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
if (isReleased) {
return;
}
if (isTouchExplorationEnabled) {
accessibilityFeatureFlags |= AccessibilityFeature.ACCESSIBLE_NAVIGATION.value;
} else {
if (!isTouchExplorationEnabled) {
setAccessibleNavigation(false);
onTouchExplorationExit();
accessibilityFeatureFlags &= ~AccessibilityFeature.ACCESSIBLE_NAVIGATION.value;
}
sendLatestAccessibilityFlagsToFlutter();
if (onAccessibilityChangeListener != null) {
onAccessibilityChangeListener.onAccessibilityChanged(
@ -551,6 +572,7 @@ public class AccessibilityBridge extends AccessibilityNodeProvider {
// Suppressing Lint warning for new API, as we are version guarding all calls to newer APIs
@SuppressLint("NewApi")
public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId) {
setAccessibleNavigation(true);
if (virtualViewId >= MIN_ENGINE_GENERATED_NODE_ID) {
// The node is in the engine generated range, and is provided by the accessibility view
// embedder.

View File

@ -124,6 +124,44 @@ public class AccessibilityBridgeTest {
assertEquals(position, outBoundsInScreen.top);
}
@Test
public void itSetsAccessibleNavigation() {
AccessibilityChannel mockChannel = mock(AccessibilityChannel.class);
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);
when(context.getPackageName()).thenReturn("test");
when(mockManager.isTouchExplorationEnabled()).thenReturn(false);
AccessibilityBridge accessibilityBridge =
setUpBridge(
/*rootAccessibilityView=*/ mockRootView,
/*accessibilityChannel=*/ mockChannel,
/*accessibilityManager=*/ mockManager,
/*contentResolver=*/ null,
/*accessibilityViewEmbedder=*/ mockViewEmbedder,
/*platformViewsAccessibilityDelegate=*/ null);
ArgumentCaptor<AccessibilityManager.TouchExplorationStateChangeListener> listenerCaptor =
ArgumentCaptor.forClass(AccessibilityManager.TouchExplorationStateChangeListener.class);
verify(mockManager).addTouchExplorationStateChangeListener(listenerCaptor.capture());
assertEquals(accessibilityBridge.getAccessibleNavigation(), false);
verify(mockChannel).setAccessibilityFeatures(0);
reset(mockChannel);
// Simulate assistive technology accessing accessibility tree.
accessibilityBridge.createAccessibilityNodeInfo(0);
verify(mockChannel).setAccessibilityFeatures(1);
assertEquals(accessibilityBridge.getAccessibleNavigation(), true);
// Simulate turning off TalkBack.
reset(mockChannel);
listenerCaptor.getValue().onTouchExplorationStateChanged(false);
verify(mockChannel).setAccessibilityFeatures(0);
assertEquals(accessibilityBridge.getAccessibleNavigation(), false);
}
@Test
public void itDoesNotContainADescriptionIfScopesRoute() {
AccessibilityBridge accessibilityBridge = setUpBridge();