Java Code Examples for androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getText()

The following examples show how to use androidx.core.view.accessibility.AccessibilityNodeInfoCompat#getText() . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 6 votes vote down vote up
/**
 * Gets the text of a <code>node</code> by returning the content description (if available) or by
 * returning the text.
 *
 * @param node The node.
 * @return The node text.
 */
public static @Nullable CharSequence getNodeText(@Nullable AccessibilityNodeInfoCompat node) {
  if (node == null) {
    return null;
  }

  // Prefer content description over text.
  // TODO: Why are we checking the trimmed length?
  final CharSequence contentDescription = node.getContentDescription();
  if (!TextUtils.isEmpty(contentDescription)
      && (TextUtils.getTrimmedLength(contentDescription) > 0)) {
    return contentDescription;
  }

  final CharSequence text = node.getText();
  if (!TextUtils.isEmpty(text) && (TextUtils.getTrimmedLength(text) > 0)) {
    return text;
  }

  return null;
}
 
Example 2
Source File: TextEditingUtils.java    From talkback with Apache License 2.0 6 votes vote down vote up
/**
 * Moves the cursor forward or backward in the text by one sentence in the given {@link
 * AccessibilityNodeInfoCompat}. The movement direction depends on the given actionId.
 *
 * @param nodeCompat The {@link AccessibilityNodeInfoCompat} in which to move the cursor
 * @param direction A {@link MovementDirection} indicating the movement direction
 * @return {@code true} if this movement is successful
 */
public static boolean moveCursorBySentenceGranularity(
    AccessibilityNodeInfoCompat nodeCompat, MovementDirection direction) {
  CharSequence text = nodeCompat.getText();
  int currentCursorPosition = nodeCompat.getTextSelectionEnd();
  int newCursorPosition = 0;
  switch (direction) {
    case DIRECTION_PREVIOUS:
      newCursorPosition = getEndOfLastSentenceBeforeIndex(text, currentCursorPosition);
      break;
    case DIRECTION_NEXT:
      newCursorPosition = getEndOfNextSentenceAfterIndex(text, currentCursorPosition);
      break;
  }
  return selectText(nodeCompat, newCursorPosition, newCursorPosition);
}
 
Example 3
Source File: WindowTransitionInfo.java    From talkback with Apache License 2.0 5 votes vote down vote up
@Nullable
private static CharSequence getWindowTitleFromNodeTree(AccessibilityWindowInfo window) {
  // Nodes to be recycled.
  AccessibilityNodeInfoCompat root = null;
  AccessibilityNodeInfoCompat windowTitleNode = null;

  try {
    root = AccessibilityNodeInfoUtils.toCompat(AccessibilityWindowInfoUtils.getRoot(window));
    if (root == null) {
      return null;
    }

    windowTitleNode = findFirstNodeWithText(root);
    if (windowTitleNode == null) {
      return null;
    }

    // TODO: Revisit the logic how we validate window title node.
    boolean isValidWindowTitleNode =
        !AccessibilityNodeInfoUtils.isOrHasMatchingAncestor(
            windowTitleNode, AccessibilityNodeInfoUtils.FILTER_ILLEGAL_TITLE_NODE_ANCESTOR);

    return isValidWindowTitleNode ? windowTitleNode.getText() : null;
  } finally {
    AccessibilityNodeInfoUtils.recycleNodes(root, windowTitleNode);
  }
}
 
Example 4
Source File: ProcessorMagnification.java    From talkback with Apache License 2.0 5 votes vote down vote up
/**
 * Returns whether a node's content starts at left or right side.
 *
 * @param node The node whose direction we want.
 * @return {@code true} if node direction is left-to-right or unknown.
 */
private static boolean isLeftToRight(AccessibilityNodeInfoCompat node) {
  if (node == null) {
    return true;
  }
  CharSequence text = node.getText();
  if (text == null || TextUtils.isEmpty(text)) {
    return true;
  }
  int direction = Character.getDirectionality(text.charAt(0));
  return (direction != Character.DIRECTIONALITY_RIGHT_TO_LEFT
      && direction != Character.DIRECTIONALITY_RIGHT_TO_LEFT_ARABIC);
}
 
Example 5
Source File: TextEditActor.java    From talkback with Apache License 2.0 5 votes vote down vote up
/** Executes and announces move cursor to end of edit-text. */
public boolean cursorToEnd(
    AccessibilityNodeInfoCompat node, boolean stopSelecting, EventId eventId) {

  if (node == null || Role.getRole(node) != Role.ROLE_EDIT_TEXT) {
    return false;
  }

  // Stop selecting.
  if (stopSelecting) {
    pipeline.returnFeedback(eventId, Feedback.focusDirection(SELECTION_MODE_OFF));
  }

  // Move cursor.
  CharSequence nodeText = node.getText();
  boolean result = false;
  if (nodeText != null) {
    result = moveCursor(node, nodeText.length(), eventId);
  }

  // Announce cursor movement.
  pipeline.returnFeedback(
      eventId,
      Feedback.speech(context.getString(R.string.notification_type_end_of_field), SPEAK_OPTIONS));

  return result;
}
 
Example 6
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Get text from an accessibility-node, including spans passed from new compat library via Bundle.
 * If span data is invalid, returns text without spans. Each returned ClickableSpanFromBundle
 * should be recycled by caller, but is probably leaked without consequences.
 */
// TODO: Replace with AccessibilityNodeInfoCompat.getText() when it provides spans.
// TODO: When androidx support library is available, change all node.getText() to use
// AccessibilityNodeInfoCompat.getText via this wrapper.

public static CharSequence getText(AccessibilityNodeInfoCompat node) {
  CharSequence nodeText = node.getText();
  if (nodeText == null) {
    return null;
  }

  // Require click action id.
  int actionId = node.getExtras().getInt(SPANS_ACTION_ID_KEY, NO_VALUE);
  if (actionId == NO_VALUE) {
    return nodeText;
  }

  // Require span start indices.
  List<Integer> starts = extrasIntList(node, SPANS_START_KEY);
  if (starts == null || starts.isEmpty()) {
    return nodeText;
  }

  // Get the rest of span data corresponding to each start point.
  List<Integer> ends = extrasIntList(node, SPANS_END_KEY, starts.size());
  List<Integer> flags = extrasIntList(node, SPANS_FLAGS_KEY, starts.size());
  List<Integer> ids = extrasIntList(node, SPANS_ID_KEY, starts.size());
  if (ends == null || flags == null || ids == null) {
    return nodeText;
  }

  // For each span... collect matching data from bundle, into spannable string.
  Spannable spannable = new SpannableString(TextUtils.substring(nodeText, 0, nodeText.length()));
  for (int i = 0; i < starts.size(); i++) {
    ClickableSpanFromBundle span = new ClickableSpanFromBundle(ids.get(i), node, actionId);
    int start = starts.get(i);
    int end = ends.get(i);
    if (end < start) {
      logError("getText", "end=%d < start=%d for i=%d", end, start, i);
      return nodeText;
    }
    spannable.setSpan(span, starts.get(i), ends.get(i), flags.get(i));
  }
  return spannable;
}
 
Example 7
Source File: FeedbackUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Gets the developer-provided text inside a node that will be used to generate spoken feedback.
 * If the node does not have any text, we attempt to get node text of any non-focusable children.
 * If there are no focusable children with text, an empty string will be returned.
 *
 * <p>Note: This method should never be called with nodes returned from
 * AccessibilityNodeInfo#obtain. These nodes do not retain children information, so this method
 * may return the incorrect text. Instead, use SwitchAccessNodeCompat#getNodeText.
 *
 * @param nodeCompat the {@link AccessibilityNodeInfoCompat} of the node from which the
 *     developer-provided text should be retrieved
 * @return the developer-provided text (content description or text) of the given node. If there
 *     is neither content description nor text inside the node or its children, then return an
 *     empty string
 */
public static String getNodeText(AccessibilityNodeInfoCompat nodeCompat) {
  CharSequence speakableText = nodeCompat.getContentDescription();

  if (StringUtils.isEmpty(speakableText)) {
    speakableText = nodeCompat.getText();
  }

  // If speakable text is empty, see if there are any non-focusable children nodes. If so, use
  // their text for the speakable text of this node. We filter out any focusable children nodes
  // to prevent duplicated speakable text from both a parent and child node.
  if (StringUtils.isEmpty(speakableText)) {
    StringBuilder builder = new StringBuilder();

    int numChildren = nodeCompat.getChildCount();
    for (int i = 0; i < numChildren; i++) {
      AccessibilityNodeInfoCompat child = nodeCompat.getChild(i);
      if ((child != null)
          && AccessibilityNodeInfoUtils.hasMinimumPixelsVisibleOnScreen(child)
          && !AccessibilityNodeInfoUtils.shouldFocusNode(child)) {
        CharSequence childText = getNodeText(child);

        if (!StringUtils.isEmpty(childText)) {
          if (builder.length() != 0) {
            builder.append(" ");
          }
          builder.append(childText);
        }
      }

      if (child != null) {
        child.recycle();
      }
    }

    speakableText = builder.toString();
  }
  if (StringUtils.isEmpty(speakableText)) {
    speakableText = "";
  }

  return speakableText.toString();
}
 
Example 8
Source File: TextEditingUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Deletes text in the given {@link AccessibilityNodeInfoCompat} with the granularity indicated in
 * the given {@link Bundle}.
 *
 * @param nodeCompat The {@link AccessibilityNodeInfo} containing the text to delete
 * @param arguments The {@link Bundle} containing the granularity arguments for deletion
 * @return {@code true} if the deletion is successful
 */
public static boolean deleteTextWithGranularity(
    AccessibilityNodeInfoCompat nodeCompat, Bundle arguments) {
  if (arguments == Bundle.EMPTY) {
    return false;
  }

  nodeCompat.refresh();
  CharSequence text = nodeCompat.getText();

  // Find the bounds of the section of text to delete.
  int deleteSectionEnd = nodeCompat.getTextSelectionEnd();
  int deleteSectionStart;
  int deleteGranularity =
      arguments.getInt(AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT);
  if (deleteGranularity == ACTION_GRANULARITY_SENTENCE) {
    deleteSectionStart = getEndOfLastSentenceBeforeIndex(text, deleteSectionEnd);
  } else if (deleteGranularity == ACTION_GRANULARITY_HIGHLIGHT) {
    deleteSectionStart = nodeCompat.getTextSelectionStart();
  } else {
    if (!PerformActionUtils.performAction(
        nodeCompat,
        AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
        arguments,
        null)) {
      return false;
    }
    nodeCompat.refresh();
    deleteSectionStart = nodeCompat.getTextSelectionEnd();
  }
  int deleteSectionLowerIndex = Math.min(deleteSectionStart, deleteSectionEnd);
  int deleteSectionUpperIndex = Math.max(deleteSectionStart, deleteSectionEnd);

  // Set text to be the entire existing text minus the section to delete.
  String oldText = (text == null) ? "" : text.toString();
  String newText =
      oldText.substring(0, deleteSectionLowerIndex) + oldText.substring(deleteSectionUpperIndex);
  arguments.putCharSequence(AccessibilityNodeInfo.ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, newText);
  if (!PerformActionUtils.performAction(
      nodeCompat, AccessibilityNodeInfo.ACTION_SET_TEXT, arguments, null)) {
    return false;
  }
  nodeCompat.refresh();

  // Place the cursor back where it was before the deletion.
  int endOfText = (text == null) ? 0 : text.length();
  int newCursorPosition = Math.min(deleteSectionLowerIndex, endOfText);
  return selectText(nodeCompat, newCursorPosition, newCursorPosition);
}
 
Example 9
Source File: TextEditingUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Select the text in the given {@link AccessibilityNodeInfoCompat} with the granularity indicated
 * in the given {@link Bundle}.
 *
 * @param nodeCompat The {@link AccessibilityNodeInfoCompat} to select text in
 * @param arguments The {@link Bundle} containing either (a) the granularity argument for
 *     selection or (b) the explicit start and end selection indices
 * @return {@code true} if the selection is successful
 */
public static boolean selectTextWithGranularity(
    AccessibilityNodeInfoCompat nodeCompat, Bundle arguments) {
  if (arguments == Bundle.EMPTY) {
    return false;
  }
  nodeCompat.refresh();
  CharSequence text = nodeCompat.getText();
  final int noGranularityPresent = -1;
  switch (arguments.getInt(
      AccessibilityNodeInfo.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT, noGranularityPresent)) {
    case ACTION_GRANULARITY_ALL:
      return selectText(nodeCompat, 0, text.length());
    case ACTION_GRANULARITY_SENTENCE:
      return extendSelectionBackOneSentence(nodeCompat);
    case noGranularityPresent:
      // Select text based on the explicit selection start and end boundaries.
      int noSelectionIndexPresent = -1;
      int selectionStart =
          arguments.getInt(
              AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_START_INT, noSelectionIndexPresent);
      int selectionEnd =
          arguments.getInt(
              AccessibilityNodeInfo.ACTION_ARGUMENT_SELECTION_END_INT, noSelectionIndexPresent);
      return (selectionStart != noSelectionIndexPresent)
          && (selectionEnd != noSelectionIndexPresent)
          && selectText(nodeCompat, selectionStart, selectionEnd);
    default:
      int currentSelectionEnd =
          Math.max(nodeCompat.getTextSelectionEnd(), nodeCompat.getTextSelectionStart());
      // If text is already selected, extend the selection. To do this, first move the cursor to
      // the beginning of the current selection. (When text is selected, and a previous movement
      // action is performed, the cursor moves by granularity from the end of the current
      // selection.)
      if (isTextSelected(nodeCompat)
          && !selectText(
              nodeCompat,
              nodeCompat.getTextSelectionStart(),
              nodeCompat.getTextSelectionStart())) {
        return false;
      }
      nodeCompat.refresh();
      if (!PerformActionUtils.performAction(
          nodeCompat,
          AccessibilityNodeInfo.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
          arguments,
          null)) {
        return false;
      }
      nodeCompat.refresh();
      return selectText(nodeCompat, nodeCompat.getTextSelectionEnd(), currentSelectionEnd);
  }
}
 
Example 10
Source File: TextEditActor.java    From talkback with Apache License 2.0 4 votes vote down vote up
/** Inserts text in edit-text. Modifies edit history. */
public boolean insert(
    AccessibilityNodeInfoCompat node, CharSequence textToInsert, EventId eventId) {

  if (node == null || Role.getRole(node) != Role.ROLE_EDIT_TEXT) {
    return false;
  }

  // Find current selected text or cursor position.
  int selectionStart = node.getTextSelectionStart();
  if (selectionStart < 0) {
    selectionStart = 0;
  }
  int selectionEnd = node.getTextSelectionEnd();
  if (selectionEnd < 0) {
    selectionEnd = selectionStart;
  }
  if (selectionEnd < selectionStart) {
    // Swap start and end to make sure they are in order.
    int newStart = selectionEnd;
    selectionEnd = selectionStart;
    selectionStart = newStart;
  }
  CharSequence currentText = node.getText();
  LogUtils.v(
      "RuleEditText",
      "insert() currentText=\"%s\"",
      (currentText == null ? "null" : currentText));
  if (currentText == null) {
    currentText = "";
  }

  // Set updated text.
  CharSequence textUpdated =
      TextUtils.concat(
          currentText.subSequence(0, selectionStart),
          textToInsert,
          currentText.subSequence(selectionEnd, currentText.length()));
  Bundle arguments = new Bundle();
  arguments.putCharSequence(ACTION_ARGUMENT_SET_TEXT_CHARSEQUENCE, textUpdated);
  boolean result = PerformActionUtils.performAction(node, ACTION_SET_TEXT, arguments, eventId);
  if (!result) {
    return false;
  }

  // Move cursor to end of inserted text.
  return moveCursor(node, selectionStart + textToInsert.length(), eventId);
}
 
Example 11
Source File: TextEditActor.java    From talkback with Apache License 2.0 4 votes vote down vote up
/** Moves cursor in edit-text. */
private boolean moveCursor(AccessibilityNodeInfoCompat node, int cursorIndex, EventId eventId) {

  if (node == null || Role.getRole(node) != Role.ROLE_EDIT_TEXT) {
    return false;
  }

  final Bundle args = new Bundle();
  boolean result = false;
  textCursorManager.forceSetCursorPosition(cursorIndex, cursorIndex);
  CharSequence nodeText = node.getText();
  if (AccessibilityNodeInfoUtils.supportsAction(
      node, AccessibilityNodeInfoCompat.ACTION_SET_SELECTION)) {
    // Perform node-action to move cursor.
    args.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, cursorIndex);
    args.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, cursorIndex);
    result =
        PerformActionUtils.performAction(
            node, AccessibilityNodeInfoCompat.ACTION_SET_SELECTION, args, eventId);
  } else if (cursorIndex == 0) {
    // Fall-back to move cursor to start of text.
    args.putInt(
        AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
        AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE);
    result =
        PerformActionUtils.performAction(
            node,
            AccessibilityNodeInfoCompat.ACTION_PREVIOUS_AT_MOVEMENT_GRANULARITY,
            args,
            eventId);
  } else if (nodeText != null && cursorIndex == node.getText().length()) {
    // Fall-back to move cursor to end of text.
    args.putInt(
        AccessibilityNodeInfoCompat.ACTION_ARGUMENT_MOVEMENT_GRANULARITY_INT,
        AccessibilityNodeInfoCompat.MOVEMENT_GRANULARITY_PAGE);
    result =
        PerformActionUtils.performAction(
            node, AccessibilityNodeInfoCompat.ACTION_NEXT_AT_MOVEMENT_GRANULARITY, args, eventId);
  }
  return result;
}
 
Example 12
Source File: GranularityTraversal.java    From talkback with Apache License 2.0 3 votes vote down vote up
/**
 * Gets the text for granularity traversal. For edit texts, it first checks for text and then the
 * hint text and then the content description.
 *
 * <p><strong>Note:</strong> For edit texts with no text and just hint text, getText() returns the
 * hint text.
 *
 * @param node on which granularity traversal has to be performed.
 * @return the content description or the text/hint text in case of edit fields, since navigation
 *     on these is not handled by the framework.
 */
public static CharSequence getIterableTextForAccessibility(AccessibilityNodeInfoCompat node) {
  if ((Role.getRole(node) == Role.ROLE_EDIT_TEXT) && !TextUtils.isEmpty(node.getText())) {
    return node.getText();
  }

  return node.getContentDescription();
}
 
Example 13
Source File: TextEditingUtils.java    From talkback with Apache License 2.0 2 votes vote down vote up
/**
 * Checks the input {@link AccessibilityNodeInfoCompat} to see if it contains selected text.
 *
 * @param nodeCompat The {@link AccessibilityNodeInfoCompat} to check
 * @return {@code true} if the input contains text and some part of the text is selected
 */
public static boolean isTextSelected(AccessibilityNodeInfoCompat nodeCompat) {
  return nodeCompat.getText() != null
      && nodeCompat.getTextSelectionEnd() != nodeCompat.getTextSelectionStart();
}
 
Example 14
Source File: TextEditingUtils.java    From talkback with Apache License 2.0 2 votes vote down vote up
/**
 * Get the {@link AccessibilityNodeInfoCompat}'s text if it is not default text (e.g. "Search..."
 * in a search box).
 *
 * <p>Note: (AccessibilityNodeInfoCompat#getTextSelectionStart == -1) indicates that there is no
 * current selection or cursor position because the text is empty (excluding default text).
 *
 * @param nodeCompat The node for which to get text
 * @return The node's text, or an empty string if the given node's text is default text
 */
public static CharSequence getNonDefaultTextForNode(AccessibilityNodeInfoCompat nodeCompat) {
  return (nodeCompat.getTextSelectionStart() == -1) ? "" : nodeCompat.getText();
}