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

The following examples show how to use androidx.core.view.accessibility.AccessibilityNodeInfoCompat#equals() . 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: OrderedTraversalController.java    From talkback with Apache License 2.0 6 votes vote down vote up
/**
 * This method is called before moving subtree. It checks if parent of that node was moved on its
 * place because it has before property to that node. In that case parent node should be moved
 * with movingTree node.
 *
 * @return top node that should be moved with movingTree node.
 */
private WorkingTree getParentsThatAreMovedBeforeOrSameNode(WorkingTree movingTree) {
  WorkingTree parent = movingTree.getParent();
  if (parent == null) {
    return movingTree;
  }

  AccessibilityNodeInfoCompat parentNode = parent.getNode();
  AccessibilityNodeInfoCompat parentNodeBefore = parentNode.getTraversalBefore();
  if (parentNodeBefore == null) {
    return movingTree;
  }

  if (parentNodeBefore.equals(movingTree.getNode())) {
    return getParentsThatAreMovedBeforeOrSameNode(parent);
  }

  return movingTree;
}
 
Example 2
Source File: AccessibilityNodeInfoRef.java    From talkback with Apache License 2.0 6 votes vote down vote up
@SuppressWarnings("BooleanMethodIsAlwaysInverted")
private boolean moveIteratorAfterNode(
    Iterator<AccessibilityNodeInfoCompat> iterator, AccessibilityNodeInfoCompat node) {
  if (node == null) {
    return false;
  }
  while (iterator.hasNext()) {
    AccessibilityNodeInfoCompat nextNode = iterator.next();
    try {
      if (node.equals(nextNode)) {
        return true;
      }
    } finally {
      AccessibilityNodeInfoUtils.recycleNodes(nextNode);
    }
  }

  return false;
}
 
Example 3
Source File: TextCursorManager.java    From talkback with Apache License 2.0 6 votes vote down vote up
private void processTextSelectionChange(AccessibilityEvent event) {
  AccessibilityNodeInfo node = event.getSource();
  if (node == null) {
    clear();
    return;
  }

  AccessibilityNodeInfoCompat compat = AccessibilityNodeInfoUtils.toCompat(node);
  if (compat.equals(mNode)) {
    compat.recycle();
    mPreviousCursorPosition = mCurrentCursorPosition;
    mCurrentCursorPosition = event.getToIndex();
  } else {
    clear();
    mNode = compat;
    mCurrentCursorPosition = event.getToIndex();
  }
}
 
Example 4
Source File: NodeDescription.java    From talkback with Apache License 2.0 6 votes vote down vote up
public boolean indexMatches(AccessibilityNodeInfoCompat node) {
  if (node == null) {
    return false;
  }
  if (indexType == INDEX_TYPE_COLLECTION) {
    CollectionItemInfoCompat itemInfo = node.getCollectionItemInfo();
    return (itemInfo != null)
        && (itemInfo.getRowIndex() == rowIndex)
        && (itemInfo.getColumnIndex() == columnIndex);
  } else {
    AccessibilityNodeInfoCompat parent = null;
    AccessibilityNodeInfoCompat child = null;
    try {
      parent = node.getParent();
      if (parent == null || parent.getChildCount() <= rawIndexInParent) {
        return false;
      }
      child = parent.getChild(rawIndexInParent);
      return node.equals(child);

    } finally {
      AccessibilityNodeInfoUtils.recycleNodes(parent, child);
    }
  }
}
 
Example 5
Source File: NodeDescription.java    From talkback with Apache License 2.0 6 votes vote down vote up
private static int getRawIndexInParent(AccessibilityNodeInfoCompat node) {
  if (node == null) {
    return UNDEFINED_INDEX;
  }
  AccessibilityNodeInfoCompat parent = node.getParent();
  if (parent == null) {
    return UNDEFINED_INDEX;
  }

  for (int i = 0; i < parent.getChildCount(); i++) {
    AccessibilityNodeInfoCompat child = parent.getChild(i);
    try {
      if (node.equals(child)) {
        return i;
      }
    } finally {
      AccessibilityNodeInfoUtils.recycleNodes(child);
    }
  }
  return UNDEFINED_INDEX;
}
 
Example 6
Source File: InputFocusInterpreter.java    From talkback with Apache License 2.0 6 votes vote down vote up
private boolean isFromSavedFocusAction(AccessibilityEvent event) {
  if (isLastFocusActionHandled()) {
    return false;
  }
  AccessibilityNodeInfoCompat node = AccessibilityNodeInfoUtils.toCompat(event.getSource());
  if (node == null) {
    return false;
  }
  long timeDiff = event.getEventTime() - focuserState.getInputFocusActionRecord().actionTime;
  boolean isFromFocusAction =
      ((timeDiff >= 0L) && (timeDiff < INPUT_FOCUS_ACTION_TIMEOUT))
          && node.equals(focuserState.getInputFocusActionRecord().inputFocusedNode);

  AccessibilityNodeInfoUtils.recycleNodes(node);
  return isFromFocusAction;
}
 
Example 7
Source File: DirectionalTraversalStrategy.java    From talkback with Apache License 2.0 5 votes vote down vote up
@Override
public @Nullable AccessibilityNodeInfoCompat findFocus(
    AccessibilityNodeInfoCompat startNode, int direction) {
  if (startNode == null) {
    return null;
  } else if (startNode.equals(mRoot)) {
    return getFirstOrderedFocus();
  }

  Rect focusedRect = new Rect();
  getAssumedRectInScreen(startNode, focusedRect);

  return findFocus(startNode, focusedRect, direction);
}
 
Example 8
Source File: AccessibilityFocusActionHistory.java    From talkback with Apache License 2.0 5 votes vote down vote up
/**
 * Matches {@link AccessibilityEvent#TYPE_VIEW_ACCESSIBILITY_FOCUSED} event to the source action.
 *
 * @param event The {@link AccessibilityEvent} queried.
 * @return The matched FocusActionRecord or null.
 */
@Nullable
public FocusActionRecord matchFocusActionRecordFromEvent(AccessibilityEvent event) {
  if (!AccessibilityEventUtils.eventMatchesAnyType(
      event, AccessibilityEvent.TYPE_VIEW_ACCESSIBILITY_FOCUSED)) {
    return null;
  }
  AccessibilityNodeInfoCompat eventNode = AccessibilityNodeInfoUtils.toCompat(event.getSource());
  if (eventNode == null) {
    return null;
  }
  long eventTime = event.getEventTime();

  tryMatchingPendingFocusAction(eventNode, eventTime);

  FocusActionRecord result = null;
  // Iterate from the newest(last) record to the eldest(first) record.
  Iterator<FocusActionRecord> iterator = focusActionRecordList.descendingIterator();
  while (iterator.hasNext()) {
    FocusActionRecord record = iterator.next();
    long timeDiff = eventTime - record.getActionTime();
    AccessibilityNodeInfoCompat recordNode = record.getFocusedNode();

    boolean timeMatches = timeDiff >= 0 && timeDiff < TIMEOUT_TOLERANCE_MS;
    boolean nodeMatches = eventNode.equals(recordNode);
    AccessibilityNodeInfoUtils.recycleNodes(recordNode);
    if (timeMatches && nodeMatches) {
      result = FocusActionRecord.copy(record);
      break;
    }
  }
  AccessibilityNodeInfoUtils.recycleNodes(eventNode);
  return result;
}
 
Example 9
Source File: TouchExplorationInterpreter.java    From talkback with Apache License 2.0 5 votes vote down vote up
/** @return {@code true} if any accessibility action is successfully performed. */
private boolean handleHoverEnterEvent(AccessibilityEvent event, EventId eventId) {
  final AccessibilityNodeInfoCompat touchedNode =
      AccessibilityNodeInfoUtils.toCompat(event.getSource());
  if (touchedNode == null) {
    // Invalid event.
    return false;
  }
  if (touchedNode.equals(lastTouchedNode)) {
    // If two consecutive Hover_Enter events have the same source node, we won't dispatch it
    // because it doesn't change anything.
    AccessibilityNodeInfoUtils.recycleNodes(touchedNode);
    return false;
  }

  setLastTouchedNode(touchedNode);

  final AccessibilityNodeInfoCompat touchedFocusableNode =
      AccessibilityNodeInfoUtils.findFocusFromHover(touchedNode);

  AccessibilityNodeInfoUtils.recycleNodes(touchedNode);
  if (touchedFocusableNode == null) {
    // . If there is no focusable node being touched, we don't dispatch empty touch
    // event immediately. Instead, we post delay to dispatch it. If we receive hover enter event
    // from other node before timeout, we cancel this post-delayed action.
    postDelayHandler.postDelayEmptyTouchAction(eventId);
  } else {
    postDelayHandler.cancelPendingEmptyTouchAction(/* dispatchPendingActionImmediately= */ false);
    return dispatchAndRecycleTouchExplorationAction(
        new TouchExplorationAction(TouchExplorationAction.HOVER_ENTER, touchedFocusableNode),
        eventId);
  }
  return false;
}
 
Example 10
Source File: ProcessorCursorState.java    From talkback with Apache License 2.0 5 votes vote down vote up
private void resetNodeCursor(AccessibilityRecordCompat record, EventId eventId) {
  AccessibilityNodeInfoCompat source = record.getSource();
  if (source != null) {
    if (source.equals(focusedNode)) {
      // Reset cursor to end if there's text.
      if (!TextUtils.isEmpty(source.getText())) {
        int end = source.getText().length();

        Bundle bundle = new Bundle();
        bundle.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_START_INT, end);
        bundle.putInt(AccessibilityNodeInfoCompat.ACTION_ARGUMENT_SELECTION_END_INT, end);

        globalVariables.setFlag(GlobalVariables.EVENT_SKIP_SELECTION_CHANGED_AFTER_FOCUSED);
        globalVariables.setFlag(GlobalVariables.EVENT_SKIP_SELECTION_CHANGED_AFTER_CURSOR_RESET);
        PerformActionUtils.performAction(
            source, AccessibilityNodeInfoCompat.ACTION_SET_SELECTION, bundle, eventId);

        SpeechController.SpeakOptions speakOptions =
            SpeechController.SpeakOptions.create()
                .setQueueMode(SpeechController.QUEUE_MODE_QUEUE)
                .setFlags(
                    FeedbackItem.FLAG_FORCED_FEEDBACK_AUDIO_PLAYBACK_ACTIVE
                        | FeedbackItem.FLAG_FORCED_FEEDBACK_MICROPHONE_ACTIVE
                        | FeedbackItem.FLAG_FORCED_FEEDBACK_SSB_ACTIVE);
        Feedback.Part.Builder part =
            Feedback.Part.builder()
                .speech(context.getString(R.string.notification_type_end_of_field), speakOptions)
                .setDelayMs(SPEECH_DELAY)
                .setInterruptGroup(CURSOR_STATE)
                .setInterruptLevel(1);
        // Not tracking performance because field feedback should already be
        // provided when field is focused.
        pipeline.returnFeedback(EVENT_ID_UNTRACKED, part);
      }
    }

    source.recycle();
  }
}
 
Example 11
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Returns the first ancestor of {@code node} that matches the {@code filter}, terminating the
 * search once it reaches {@code end}. The search is exclusive of both {@code node} and {@code
 * end}. Returns {@code null} if no nodes match.
 *
 * <p><strong>Note:</strong> Caller is responsible for recycling the returned node.
 */
private static @Nullable AccessibilityNodeInfoCompat getMatchingAncestor(
    AccessibilityNodeInfoCompat node,
    AccessibilityNodeInfoCompat end,
    Filter<AccessibilityNodeInfoCompat> filter) {
  if (node == null) {
    return null;
  }

  final HashSet<AccessibilityNodeInfoCompat> ancestors = new HashSet<>();

  try {
    ancestors.add(AccessibilityNodeInfoCompat.obtain(node));
    node = node.getParent();

    while (node != null) {
      if (!ancestors.add(node)) {
        // Already seen this node, so abort!
        node.recycle();
        return null;
      }

      if (end != null && node.equals(end)) {
        // Reached the end node, so abort!
        // Don't recycle the node here, it was added to ancestors and will be recycled.
        return null;
      }

      if (filter.accept(node)) {
        // Send a copy since node gets recycled.
        return AccessibilityNodeInfoCompat.obtain(node);
      }

      node = node.getParent();
    }
  } finally {
    recycleNodes(ancestors);
  }

  return null;
}
 
Example 12
Source File: DirectionalTraversalStrategy.java    From talkback with Apache License 2.0 4 votes vote down vote up
public @Nullable AccessibilityNodeInfoCompat findFocus(
    AccessibilityNodeInfoCompat focused, Rect focusedRect, int direction) {
  // Using roughly the same algorithm as
  // frameworks/base/core/java/android/view/FocusFinder.java#findNextFocusInAbsoluteDirection

  Rect bestCandidateRect = new Rect(focusedRect);
  switch (direction) {
    case TraversalStrategy.SEARCH_FOCUS_LEFT:
      bestCandidateRect.offset(focusedRect.width() + 1, 0);
      break;
    case TraversalStrategy.SEARCH_FOCUS_RIGHT:
      bestCandidateRect.offset(-(focusedRect.width() + 1), 0);
      break;
    case TraversalStrategy.SEARCH_FOCUS_UP:
      bestCandidateRect.offset(0, focusedRect.height() + 1);
      break;
    case TraversalStrategy.SEARCH_FOCUS_DOWN:
      bestCandidateRect.offset(0, -(focusedRect.height() + 1));
      break;
    default: // fall out
  }

  AccessibilityNodeInfoCompat closest = null;
  for (AccessibilityNodeInfoCompat focusable : mFocusables) {
    // Skip the currently-focused view.
    if (focusable.equals(focused) || focusable.equals(mRoot)) {
      continue;
    }

    Rect otherRect = new Rect();
    getAssumedRectInScreen(focusable, otherRect);

    if (isBetterCandidate(direction, focusedRect, otherRect, bestCandidateRect)) {
      bestCandidateRect.set(otherRect);
      closest = focusable;
    }
  }

  if (closest != null) {
    return AccessibilityNodeInfoCompat.obtain(closest);
  }

  return null;
}
 
Example 13
Source File: TraversalStrategyUtils.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Utility method for determining if a searching past a particular node will fall off the edge of
 * a scrollable container.
 *
 * @param cursor Node to check.
 * @param ignoreDescendantsOfCursor Whether to ignore descendants of cursor when search down the
 *     node tree.
 * @param direction The direction in which to move from the cursor.
 * @param filter Filter used to validate list-type ancestors.
 * @param traversalStrategy - traversal strategy that is used to define order of node
 * @return {@code true} if focusing search in the specified direction will fall off the edge of
 *     the container.
 */
private static boolean isMatchingEdgeListItem(
    final AccessibilityNodeInfoCompat cursor,
    boolean ignoreDescendantsOfCursor,
    @TraversalStrategy.SearchDirection int direction,
    Filter<AccessibilityNodeInfoCompat> filter,
    TraversalStrategy traversalStrategy) {
  AccessibilityNodeInfoCompat ancestor = null;
  AccessibilityNodeInfoCompat nextFocusNode = null;
  AccessibilityNodeInfoCompat searchedAncestor = null;
  AccessibilityNodeInfoCompat webViewNode = null;

  try {
    ancestor = AccessibilityNodeInfoUtils.getMatchingAncestor(cursor, filter);
    if (ancestor == null) {
      // Not contained in a scrollable list.
      return false;
    }
    Filter<AccessibilityNodeInfoCompat> focusNodeFilter =
        AccessibilityNodeInfoUtils.FILTER_SHOULD_FOCUS;
    if (ignoreDescendantsOfCursor) {
      focusNodeFilter =
          focusNodeFilter.and(
              new Filter<AccessibilityNodeInfoCompat>() {
                @Override
                public boolean accept(AccessibilityNodeInfoCompat obj) {
                  return !AccessibilityNodeInfoUtils.hasAncestor(obj, cursor);
                }
              });
    }
    nextFocusNode = searchFocus(traversalStrategy, cursor, direction, focusNodeFilter);
    if ((nextFocusNode == null) || nextFocusNode.equals(ancestor)) {
      // Can't move from this position.
      return true;
    }

    // if nextFocusNode is in WebView and not visible to user we still could set
    // accessibility  focus on it and WebView scrolls itself to show newly focused item
    // on the screen. But there could be situation that node is inside WebView bounds but
    // WebView is [partially] outside the screen bounds. In that case we don't ask WebView
    // to set accessibility focus but try to scroll scrollable parent to get the WebView
    // with nextFocusNode inside it to the screen bounds.
    if (!nextFocusNode.isVisibleToUser()
        && WebInterfaceUtils.hasNativeWebContent(nextFocusNode)) {
      webViewNode =
          AccessibilityNodeInfoUtils.getMatchingAncestor(
              nextFocusNode,
              new Filter<AccessibilityNodeInfoCompat>() {
                @Override
                public boolean accept(AccessibilityNodeInfoCompat node) {
                  return Role.getRole(node) == Role.ROLE_WEB_VIEW;
                }
              });

      if (webViewNode != null
          && (!webViewNode.isVisibleToUser()
              || isNodeInBoundsOfOther(webViewNode, nextFocusNode))) {
        return true;
      }
    }

    searchedAncestor = AccessibilityNodeInfoUtils.getMatchingAncestor(nextFocusNode, filter);
    while (searchedAncestor != null) {
      if (ancestor.equals(searchedAncestor)) {
        return false;
      }
      AccessibilityNodeInfoCompat temp = searchedAncestor;
      searchedAncestor = AccessibilityNodeInfoUtils.getMatchingAncestor(searchedAncestor, filter);
      temp.recycle();
    }
    // Moves outside of the scrollable container.
    return true;
  } finally {
    AccessibilityNodeInfoUtils.recycleNodes(
        ancestor, nextFocusNode, searchedAncestor, webViewNode);
  }
}
 
Example 14
Source File: FocusManagerInternal.java    From talkback with Apache License 2.0 4 votes vote down vote up
/**
 * Navigate directional from pivot to target html element.
 *
 * <p><strong>Note: </strong> Caller is responsible to recycle {@code pivot}.
 */
boolean navigateToHtmlElement(
    AccessibilityNodeInfoCompat pivot,
    int direction,
    String htmlElement,
    FocusActionInfo focusActionInfo,
    EventId eventId) {
  long currentTime = SystemClock.uptimeMillis();
  boolean result =
      WebInterfaceUtils.performNavigationToHtmlElementAction(
          pivot, direction, htmlElement, eventId);
  if (!result) {
    return false;
  }

  // Cache the accessibility focus action history.

  AccessibilityNodeInfoCompat root = null;
  AccessibilityNodeInfoCompat newFocus = null;

  try {
    root = AccessibilityNodeInfoUtils.getRoot(pivot);
    newFocus = (root == null) ? null : root.findFocus(AccessibilityNodeInfo.FOCUS_ACCESSIBILITY);

    LogUtils.d(
        TAG,
        "Navigate in web:result=%s\nNode:%s\nFocusActionInfo:%s",
        newFocus,
        pivot,
        focusActionInfo);

    FocusActionInfo updatedFocusActionInfo =
        updateFocusActionInfoIfNecessary(focusActionInfo, newFocus);

    if (newFocus == null || pivot.equals(newFocus)) {
      // The focus should have been changed, otherwise we have to wait for the next
      // TYPE_VIEW_ACCESSIBILITY_FOCUSED event to get the correct focused node.
      // Usually this logic will not be invoked. A known case for this is navigating in Firefox.
      history.onPendingAccessibilityFocusActionOnWebElement(
          updatedFocusActionInfo, currentTime, screenStateMonitor.getCurrentScreenState());
    } else {
      history.onAccessibilityFocusAction(
          newFocus,
          updatedFocusActionInfo,
          currentTime,
          screenStateMonitor.getCurrentScreenState());
    }
  } finally {
    AccessibilityNodeInfoUtils.recycleNodes(root, newFocus);
  }
  return true;
}
 
Example 15
Source File: RuleViewPager.java    From talkback with Apache License 2.0 4 votes vote down vote up
@Override
public List<ContextMenuItem> getMenuItemsForNode(
    TalkBackService service,
    ContextMenuItemBuilder menuItemBuilder,
    AccessibilityNodeInfoCompat node,
    boolean includeAncestors) {
  final List<ContextMenuItem> items = new ArrayList<>();

  AccessibilityNodeInfoCompat pagerNode = null;

  try {
    pagerNode = AccessibilityNodeInfoUtils.getSelfOrMatchingAncestor(node, FILTER_PAGED);
    if (pagerNode == null) {
      return items;
    }

    if (!includeAncestors && !pagerNode.equals(node)) {
      return items;
    }

    addPageActions(items, service, menuItemBuilder, pagerNode);

    // Check for scroll actions if no page items were added. A node with page actions shouldn't be
    // using scroll actions to navigate pages.
    if (items.isEmpty()) {
      addScrollActions(items, service, menuItemBuilder, pagerNode);
    }

    if (items.isEmpty()) {
      return items;
    }

    final AccessibilityNodeInfoCompat pagerNodeClone =
        AccessibilityNodeInfoCompat.obtain(pagerNode);
    final ViewPagerItemClickListener itemClickListener =
        new ViewPagerItemClickListener(pagerNodeClone);
    for (ContextMenuItem item : items) {
      item.setOnMenuItemClickListener(itemClickListener);
    }

    return items;
  } finally {
    AccessibilityNodeInfoUtils.recycleNodes(pagerNode);
  }
}