Class AccessibilityBridge


public class AccessibilityBridge extends AccessibilityNodeProvider
Bridge between Android's OS accessibility system and Flutter's accessibility system.

An AccessibilityBridge requires:

  • A real Android View, called the rootAccessibilityView, which contains a Flutter UI. The rootAccessibilityView is required at the time of AccessibilityBridge's instantiation and is held for the duration of AccessibilityBridge's lifespan. AccessibilityBridge invokes various accessibility methods on the rootAccessibilityView, e.g., View.onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo). The rootAccessibilityView is expected to notify the AccessibilityBridge of relevant interactions: onAccessibilityHoverEvent(MotionEvent), reset(), updateSemantics(ByteBuffer, String[], ByteBuffer[]), and updateCustomAccessibilityActions(ByteBuffer, String[])
  • An AccessibilityChannel that is connected to the running Flutter app.
  • Android's AccessibilityManager to query and listen for accessibility settings.
  • Android's ContentResolver to listen for changes to system animation settings.
The AccessibilityBridge causes Android to treat Flutter SemanticsNodes as if they were accessible Android Views. Accessibility requests may be sent from a Flutter widget to the Android OS, as if it were an Android View, and accessibility events may be consumed by a Flutter widget, as if it were an Android View. AccessibilityBridge refers to Flutter's accessible widgets as "virtual views" and identifies them with "virtual view IDs".
  • Constructor Details

  • Method Details

    • getHoveredObjectId

      @VisibleForTesting public int getHoveredObjectId()
    • getAccessibleNavigation

      @VisibleForTesting public boolean getAccessibleNavigation()
    • release

      public void release()
      Disconnects any listeners and/or delegates that were initialized in AccessibilityBridge's constructor, or added after.

      Do not use this instance after invoking release. The behavior of any method invoked on this AccessibilityBridge after invoking release() is undefined.

    • isAccessibilityEnabled

      public boolean isAccessibilityEnabled()
      Returns true if the Android OS currently has accessibility enabled, false otherwise.
    • isTouchExplorationEnabled

      public boolean isTouchExplorationEnabled()
      Returns true if the Android OS currently has touch exploration enabled, false otherwise.
    • setOnAccessibilityChangeListener

      public void setOnAccessibilityChangeListener(@Nullable AccessibilityBridge.OnAccessibilityChangeListener listener)
      Sets a listener on this AccessibilityBridge, which is notified whenever accessibility activation, or touch exploration activation changes.
    • obtainAccessibilityNodeInfo

      @VisibleForTesting public AccessibilityNodeInfo obtainAccessibilityNodeInfo(View rootView)
    • obtainAccessibilityNodeInfo

      @VisibleForTesting public AccessibilityNodeInfo obtainAccessibilityNodeInfo(View rootView, int virtualViewId)
    • createAccessibilityNodeInfo

      public AccessibilityNodeInfo createAccessibilityNodeInfo(int virtualViewId)
      Returns AccessibilityNodeInfo for the view corresponding to the given virtualViewId.

      This method is invoked by Android's accessibility system when Android needs accessibility info for a given view.

      When a virtualViewId of View.NO_ID is requested, accessibility node info is returned for our rootAccessibilityView. Otherwise, Flutter's semantics tree, represented by flutterSemanticsTree, is searched for a AccessibilityBridge.SemanticsNode with the given virtualViewId. If no such AccessibilityBridge.SemanticsNode is found, then this method returns null. If the desired AccessibilityBridge.SemanticsNode is found, then an AccessibilityNodeInfo is obtained from the rootAccessibilityView, filled with appropriate info, and then returned.

      Depending on the type of Flutter SemanticsNode that is requested, the returned AccessibilityNodeInfo pretends that the SemanticsNode in question comes from a specialize Android view, e.g., AccessibilityBridge.Flag.IS_TEXT_FIELD maps to android.widget.EditText, AccessibilityBridge.Flag.IS_BUTTON maps to android.widget.Button, and AccessibilityBridge.Flag.IS_IMAGE maps to android.widget.ImageView. In the case that no specialized view applies, the returned AccessibilityNodeInfo pretends that it represents a android.view.View.

      Overrides:
      createAccessibilityNodeInfo in class AccessibilityNodeProvider
    • performAction

      public boolean performAction(int virtualViewId, int accessibilityAction, @Nullable Bundle arguments)
      Instructs the view represented by virtualViewId to carry out the desired accessibilityAction, perhaps configured by additional arguments.

      This method is invoked by Android's accessibility system. This method returns true if the desired SemanticsNode was found and was capable of performing the desired action, false otherwise.

      In a traditional Android app, the given view ID refers to a View within an Android View hierarchy. Flutter does not have an Android View hierarchy, therefore the given view ID is a virtualViewId that refers to a SemanticsNode within a Flutter app. The given arguments of this method are forwarded from Android to Flutter.

      Overrides:
      performAction in class AccessibilityNodeProvider
    • findFocus

      public AccessibilityNodeInfo findFocus(int focus)
      Finds the view in a hierarchy that currently has the given type of focus.

      This method is invoked by Android's accessibility system.

      Flutter does not have an Android View hierarchy. Therefore, Flutter conceptually handles this request by searching its semantics tree for the given focus, represented by flutterSemanticsTree. In practice, this AccessibilityBridge always caches any active accessibilityFocusedSemanticsNode and inputFocusedSemanticsNode. Therefore, no searching is necessary. This method directly inspects the given focus type to return one of the cached nodes, null if the cached node is null, or null if a different focus type is requested.

      Overrides:
      findFocus in class AccessibilityNodeProvider
    • onAccessibilityHoverEvent

      public boolean onAccessibilityHoverEvent(MotionEvent event)
      A hover MotionEvent has occurred in the View that corresponds to this AccessibilityBridge.

      This method returns true if Flutter's accessibility system handled the hover event, false otherwise.

      This method should be invoked from the corresponding View's View.onHoverEvent(MotionEvent).

    • onAccessibilityHoverEvent

      public boolean onAccessibilityHoverEvent(MotionEvent event, boolean ignorePlatformViews)
      A hover MotionEvent has occurred in the View that corresponds to this AccessibilityBridge.

      If ignorePlatformViews is true, if hit testing for the event finds a platform view, the event will not be handled. This is useful when handling accessibility events for views overlaying platform views. See PlatformOverlayView for details.

      This method returns true if Flutter's accessibility system handled the hover event, false otherwise.

      This method should be invoked from the corresponding View's View.onHoverEvent(MotionEvent).

    • sendAccessibilityEvent

      @VisibleForTesting public void sendAccessibilityEvent(int viewId, int eventType)
      Sends an accessibility event of the given eventType to Android's accessibility system with the given viewId represented as the source of the event.

      The given viewId may either belong to rootAccessibilityView, or any Flutter AccessibilityBridge.SemanticsNode.

    • obtainAccessibilityEvent

      @VisibleForTesting public AccessibilityEvent obtainAccessibilityEvent(int eventType)
    • reset

      public void reset()
      Resets the AccessibilityBridge:
      • Clears flutterSemanticsTree, the Android cache of Flutter's semantics tree
      • Releases focus on any active accessibilityFocusedSemanticsNode
      • Clears any hovered SemanticsNode
      • Sends a AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED event
    • externalViewRequestSendAccessibilityEvent

      public boolean externalViewRequestSendAccessibilityEvent(View embeddedView, View eventOrigin, AccessibilityEvent event)
      Delegates handling of ViewParent.requestSendAccessibilityEvent(android.view.View, android.view.accessibility.AccessibilityEvent) to the accessibility bridge.

      This is used by embedded platform views to propagate accessibility events from their view hierarchy to the accessibility bridge.

      As the embedded view doesn't have to be the only View in the embedded hierarchy (it can have child views) and the event might have been originated from any view in this hierarchy, this method gets both a reference to the embedded platform view, and a reference to the view from its hierarchy that sent the event.

      Parameters:
      embeddedView - the embedded platform view for which the event is delegated
      eventOrigin - the view in the embedded view's hierarchy that sent the event.
      Returns:
      True if the event was sent.