public boolean performAction()

in shell/platform/android/io/flutter/view/AccessibilityBridge.java [950:1171]


  public boolean performAction(
      int virtualViewId, int accessibilityAction, @Nullable Bundle arguments) {
    if (virtualViewId >= MIN_ENGINE_GENERATED_NODE_ID) {
      // The node is in the engine generated range, and is handled by the accessibility view
      // embedder.
      boolean didPerform =
          accessibilityViewEmbedder.performAction(virtualViewId, accessibilityAction, arguments);
      if (didPerform
          && accessibilityAction == AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS) {
        embeddedAccessibilityFocusedNodeId = null;
      }
      return didPerform;
    }
    SemanticsNode semanticsNode = flutterSemanticsTree.get(virtualViewId);
    if (semanticsNode == null) {
      return false;
    }
    switch (accessibilityAction) {
      case AccessibilityNodeInfo.ACTION_CLICK:
        {
          // Note: TalkBack prior to Oreo doesn't use this handler and instead simulates a
          //     click event at the center of the SemanticsNode. Other a11y services might go
          //     through this handler though.
          accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.TAP);
          return true;
        }
      case AccessibilityNodeInfo.ACTION_LONG_CLICK:
        {
          // Note: TalkBack doesn't use this handler and instead simulates a long click event
          //     at the center of the SemanticsNode. Other a11y services might go through this
          //     handler though.
          accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.LONG_PRESS);
          return true;
        }
      case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD:
        {
          if (semanticsNode.hasAction(Action.SCROLL_UP)) {
            accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SCROLL_UP);
          } else if (semanticsNode.hasAction(Action.SCROLL_LEFT)) {
            // TODO(ianh): bidi support using textDirection
            accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SCROLL_LEFT);
          } else if (semanticsNode.hasAction(Action.INCREASE)) {
            semanticsNode.value = semanticsNode.increasedValue;
            semanticsNode.valueAttributes = semanticsNode.increasedValueAttributes;
            // Event causes Android to read out the updated value.
            sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_SELECTED);
            accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.INCREASE);
          } else {
            return false;
          }
          return true;
        }
      case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD:
        {
          if (semanticsNode.hasAction(Action.SCROLL_DOWN)) {
            accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SCROLL_DOWN);
          } else if (semanticsNode.hasAction(Action.SCROLL_RIGHT)) {
            // TODO(ianh): bidi support using textDirection
            accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SCROLL_RIGHT);
          } else if (semanticsNode.hasAction(Action.DECREASE)) {
            semanticsNode.value = semanticsNode.decreasedValue;
            semanticsNode.valueAttributes = semanticsNode.decreasedValueAttributes;
            // Event causes Android to read out the updated value.
            sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_SELECTED);
            accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.DECREASE);
          } else {
            return false;
          }
          return true;
        }
      case AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY:
        {
          // Text selection APIs aren't available until API 18. We can't handle the case here so
          // return false
          // instead. It's extremely unlikely that this case would ever be triggered in the first
          // place in API <
          // 18.
          if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            return false;
          }
          return performCursorMoveAction(semanticsNode, virtualViewId, arguments, false);
        }
      case AccessibilityNodeInfo.ACTION_NEXT_AT_MOVEMENT_GRANULARITY:
        {
          // Text selection APIs aren't available until API 18. We can't handle the case here so
          // return false
          // instead. It's extremely unlikely that this case would ever be triggered in the first
          // place in API <
          // 18.
          if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            return false;
          }
          return performCursorMoveAction(semanticsNode, virtualViewId, arguments, true);
        }
      case AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS:
        {
          // Focused semantics node must be reset before sending the
          // TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED event. Otherwise,
          // TalkBack may think the node is still focused.
          if (accessibilityFocusedSemanticsNode != null
              && accessibilityFocusedSemanticsNode.id == virtualViewId) {
            accessibilityFocusedSemanticsNode = null;
          }
          if (embeddedAccessibilityFocusedNodeId != null
              && embeddedAccessibilityFocusedNodeId == virtualViewId) {
            embeddedAccessibilityFocusedNodeId = null;
          }
          accessibilityChannel.dispatchSemanticsAction(
              virtualViewId, Action.DID_LOSE_ACCESSIBILITY_FOCUS);
          sendAccessibilityEvent(
              virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUS_CLEARED);
          return true;
        }
      case AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS:
        {
          if (accessibilityFocusedSemanticsNode == null) {
            // When Android focuses a node, it doesn't invalidate the view.
            // (It does when it sends ACTION_CLEAR_ACCESSIBILITY_FOCUS, so
            // we only have to worry about this when the focused node is null.)
            rootAccessibilityView.invalidate();
          }
          // Focused semantics node must be set before sending the TYPE_VIEW_ACCESSIBILITY_FOCUSED
          // event. Otherwise, TalkBack may think the node is not focused yet.
          accessibilityFocusedSemanticsNode = semanticsNode;

          accessibilityChannel.dispatchSemanticsAction(
              virtualViewId, Action.DID_GAIN_ACCESSIBILITY_FOCUS);
          sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED);

          if (semanticsNode.hasAction(Action.INCREASE)
              || semanticsNode.hasAction(Action.DECREASE)) {
            // SeekBars only announce themselves after this event.
            sendAccessibilityEvent(virtualViewId, AccessibilityEvent.TYPE_VIEW_SELECTED);
          }

          return true;
        }
      case ACTION_SHOW_ON_SCREEN:
        {
          accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.SHOW_ON_SCREEN);
          return true;
        }
      case AccessibilityNodeInfo.ACTION_SET_SELECTION:
        {
          // Text selection APIs aren't available until API 18. We can't handle the case here so
          // return false
          // instead. It's extremely unlikely that this case would ever be triggered in the first
          // place in API <
          // 18.
          if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR2) {
            return false;
          }
          final Map<String, Integer> selection = new HashMap<>();
          final boolean hasSelection =
              arguments != null
                  && arguments.containsKey(
                      AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT)
                  && arguments.containsKey(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT);
          if (hasSelection) {
            selection.put(
                "base",
                arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT));
            selection.put(
                "extent",
                arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT));
          } else {
            // Clear the selection
            selection.put("base", semanticsNode.textSelectionExtent);
            selection.put("extent", semanticsNode.textSelectionExtent);
          }
          accessibilityChannel.dispatchSemanticsAction(
              virtualViewId, Action.SET_SELECTION, selection);
          // The voice access expects the semantics node to update immediately. We update the
          // semantics node based on prediction. If the result is incorrect, it will be updated in
          // the next frame.
          SemanticsNode node = flutterSemanticsTree.get(virtualViewId);
          node.textSelectionBase = selection.get("base");
          node.textSelectionExtent = selection.get("extent");
          return true;
        }
      case AccessibilityNodeInfo.ACTION_COPY:
        {
          accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.COPY);
          return true;
        }
      case AccessibilityNodeInfo.ACTION_CUT:
        {
          accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.CUT);
          return true;
        }
      case AccessibilityNodeInfo.ACTION_PASTE:
        {
          accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.PASTE);
          return true;
        }
      case AccessibilityNodeInfo.ACTION_DISMISS:
        {
          accessibilityChannel.dispatchSemanticsAction(virtualViewId, Action.DISMISS);
          return true;
        }
      case AccessibilityNodeInfo.ACTION_SET_TEXT:
        {
          // Set text APIs aren't available until API 21. We can't handle the case here so
          // return false instead. It's extremely unlikely that this case would ever be
          // triggered in the first place in API < 21.
          if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            return false;
          }
          return performSetText(semanticsNode, virtualViewId, arguments);
        }
      default:
        // might be a custom accessibility accessibilityAction.
        final int flutterId = accessibilityAction - FIRST_RESOURCE_ID;
        CustomAccessibilityAction contextAction = customAccessibilityActions.get(flutterId);
        if (contextAction != null) {
          accessibilityChannel.dispatchSemanticsAction(
              virtualViewId, Action.CUSTOM_ACTION, contextAction.id);
          return true;
        }
    }
    return false;
  }