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

The following examples show how to use androidx.core.view.accessibility.AccessibilityNodeInfoCompat#isVisibleToUser() . 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: AccessibilityUtil.java    From screenshot-tests-for-android with Apache License 2.0 6 votes vote down vote up
/**
 * Determines if the provided {@link View} and {@link AccessibilityNodeInfoCompat} meet the
 * criteria for gaining accessibility focus.
 *
 * <p>Note: this is evaluating general focusability by accessibility services, and does not mean
 * this view will be guaranteed to be focused by specific services such as Talkback. For Talkback
 * focusability, see {@link #isTalkbackFocusable(View)}
 *
 * @param view The {@link View} to evaluate
 * @param node The {@link AccessibilityNodeInfoCompat} to evaluate
 * @return {@code true} if it is possible to gain accessibility focus
 */
public static boolean isAccessibilityFocusable(
    @Nullable AccessibilityNodeInfoCompat node, @Nullable View view) {
  if (node == null || view == null) {
    return false;
  }

  // Never focus invisible nodes.
  if (!node.isVisibleToUser()) {
    return false;
  }

  // Always focus "actionable" nodes.
  if (isActionableForAccessibility(node)) {
    return true;
  }

  // only focus top-level list items with non-actionable speaking children.
  return isTopLevelScrollItem(node, view) && isSpeakingNode(node, view);
}
 
Example 2
Source File: AccessibilityUtil.java    From screenshot-tests-for-android with Apache License 2.0 6 votes vote down vote up
/**
 * Returns whether a View has any children that are visible.
 *
 * @param view The {@link View} to evaluate
 * @return {@code true} if node has any visible children
 */
public static boolean hasVisibleChildren(View view) {
  if (!(view instanceof ViewGroup)) {
    return false;
  }

  ViewGroup viewGroup = (ViewGroup) view;
  int childCount = viewGroup.getChildCount();
  for (int i = 0; i < childCount; ++i) {
    AccessibilityNodeInfoCompat childNodeInfo = createNodeInfoFromView(viewGroup.getChildAt(i));
    if (childNodeInfo != null) {
      try {
        if (childNodeInfo.isVisibleToUser()) {
          return true;
        }
      } finally {
        childNodeInfo.recycle();
      }
    }
  }

  return false;
}
 
Example 3
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 6 votes vote down vote up
private static boolean hasVisibleChildren(AccessibilityNodeInfoCompat node) {
  int childCount = node.getChildCount();
  for (int i = 0; i < childCount; ++i) {
    AccessibilityNodeInfoCompat child = node.getChild(i);
    if (child != null) {
      try {
        if (child.isVisibleToUser()) {
          return true;
        }
      } finally {
        child.recycle();
      }
    }
  }

  return false;
}
 
Example 4
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 6 votes vote down vote up
public static int countVisibleChildren(AccessibilityNodeInfoCompat node) {
  if (node == null) {
    return 0;
  }
  int childCount = node.getChildCount();
  int childVisibleCount = 0;
  for (int i = 0; i < childCount; ++i) {
    AccessibilityNodeInfoCompat child = node.getChild(i);
    if (child != null) {
      try {
        if (child.isVisibleToUser()) {
          ++childVisibleCount;
        }
      } finally {
        child.recycle();
      }
    }
  }
  return childVisibleCount;
}
 
Example 5
Source File: ProcessorCursorState.java    From talkback with Apache License 2.0 6 votes vote down vote up
private void touchStart(AccessibilityEvent event, EventId eventId) {
  // Detect if the node is visible and editing; if so, then show the overlay with a delay.
  AccessibilityNodeInfoCompat refreshedNode = AccessibilityNodeInfoUtils.refreshNode(focusedNode);
  if (refreshedNode != null) {
    boolean focused;
    AccessibilityWindowInfoCompat window = AccessibilityNodeInfoUtils.getWindow(refreshedNode);
    focused =
        refreshedNode.isVisibleToUser()
            && refreshedNode.isFocused()
            && window != null
            && window.isFocused();
    if (focused) {
      Rect r = new Rect();
      refreshedNode.getBoundsInScreen(r);
      overlay.showDelayed(r);
    }
    refreshedNode.recycle();

    overlay.onAccessibilityEvent(event, eventId);
  }
}
 
Example 6
Source File: ScrollFeedbackManager.java    From talkback with Apache License 2.0 5 votes vote down vote up
private static CharSequence getSelectedPageTitle(AccessibilityNodeInfo node) {
  // We need to refresh() after the scroll to get an accurate page title
  if (node == null) {
    return null;
  }

  AccessibilityNodeInfoCompat nodeCompat = AccessibilityNodeInfoUtils.toCompat(node);
  nodeCompat.refresh();

  int numChildren = nodeCompat.getChildCount(); // Not the number of pages!
  CharSequence title = null;
  for (int i = 0; i < numChildren; ++i) {
    AccessibilityNodeInfoCompat child = nodeCompat.getChild(i);
    if (child != null) {
      try {
        if (child.isVisibleToUser()) {
          if (title == null) {
            // Try to roughly match RulePagerPage, which uses getNodeText
            // (but completely matching all the time is not critical).
            title = AccessibilityNodeInfoUtils.getNodeText(child);
          } else {
            // Multiple visible children, abort.
            return null;
          }
        }
      } finally {
        child.recycle();
      }
    }
  }

  return title;
}
 
Example 7
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 5 votes vote down vote up
public static @Nullable CharSequence getSelectedPageTitle(AccessibilityNodeInfoCompat viewPager) {
  if ((viewPager == null) || (Role.getRole(viewPager) != Role.ROLE_PAGER)) {
    return null;
  }

  int numChildren = viewPager.getChildCount(); // Not the number of pages!
  CharSequence title = null;
  for (int i = 0; i < numChildren; ++i) {
    AccessibilityNodeInfoCompat child = viewPager.getChild(i);
    if (child != null) {
      try {
        if (child.isVisibleToUser()) {
          if (title == null) {
            // Try to roughly match RulePagerPage, which uses getNodeText
            // (but completely matching all the time is not critical).
            title = getNodeText(child);
          } else {
            // Multiple visible children, abort.
            return null;
          }
        }
      } finally {
        recycleNodes(child);
      }
    }
  }

  return title;
}
 
Example 8
Source File: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 5 votes vote down vote up
/** Helper method that returns {@code true} if the specified node is visible to the user */
public static boolean isVisible(AccessibilityNodeInfoCompat node) {
  // We need to move focus to invisible node in WebView to scroll it but we don't want to
  // move focus if WebView itself is invisible.
  return node != null
      && (node.isVisibleToUser()
          || (WebInterfaceUtils.isWebContainer(node)
              && Role.getRole(node) != Role.ROLE_WEB_VIEW));
}
 
Example 9
Source File: FocusActor.java    From talkback with Apache License 2.0 5 votes vote down vote up
/** Restore focus with the node cached before context menu or dialog appeared. */
public boolean restoreFocus(EventId eventId) {
  AccessibilityNodeInfoCompat nodeToRestoreFocus = history.popCachedNodeToRestoreFocus();
  if (nodeToRestoreFocus == null) {
    return false;
  }

  try {
    if (!nodeToRestoreFocus.refresh() || !nodeToRestoreFocus.isVisibleToUser()) {
      return false;
    }

    return AccessibilityNodeInfoUtils.isInWindow(
            nodeToRestoreFocus, nodeToRestoreFocus.getWindow())
        && focusManagerInternal.setAccessibilityFocus(
            nodeToRestoreFocus,
            /* forceRefocusIfAlreadyFocused= */ false,
            FocusActionInfo.builder()
                .setSourceAction(FocusActionInfo.SCREEN_STATE_CHANGE)
                .setInitialFocusType(FocusActionInfo.RESTORED_LAST_FOCUS)
                .build(),
            eventId);

  } finally {
    AccessibilityNodeInfoUtils.recycleNodes(nodeToRestoreFocus);
  }
}
 
Example 10
Source File: FocusProcessorForScreenStateChange.java    From talkback with Apache License 2.0 5 votes vote down vote up
/**
 * Restores last focus from {@link AccessibilityFocusActionHistory} to the active window. Caller
 * should recycle {@code root}.
 *
 * @param root root node in the active window
 * @param windowType current active window type
 * @param windowId current active window id
 * @param windowTitle current active window title
 * @param eventId event id
 * @return {@code true} if successfully restore and set accessibility focus on the node.
 */
protected boolean restoreLastFocusedNode(
    AccessibilityNodeInfoCompat root,
    int windowType,
    int windowId,
    @Nullable CharSequence windowTitle,
    EventId eventId) {
  if (windowType == AccessibilityWindowInfo.TYPE_SYSTEM) {
    // Don't restore focus in system window. A exemption is when context menu closes, we might
    // restore focus in a system window in restoreFocusForContextMenu().
    LogUtils.d(TAG, "Do not restore focus in system ui window.");
    return false;
  }

  AccessibilityFocusActionHistory.Reader history = actorState.getFocusHistory();
  final FocusActionRecord lastFocusAction =
      history.getLastFocusActionRecordInWindow(windowId, windowTitle);
  if (lastFocusAction == null) {
    return false;
  }
  AccessibilityNodeInfoCompat nodeToRestoreFocus = getNodeToRestoreFocus(root, lastFocusAction);
  try {
    return (nodeToRestoreFocus != null)
        && nodeToRestoreFocus.isVisibleToUser()
        // When a pane changes, the nodes not in the pane become out of the window even though
        // they are still visible to user. The window id and title don't change, so the last
        // focused node, which searches from getFocusHistory(), may not be in the window.
        && AccessibilityNodeInfoUtils.isInWindow(nodeToRestoreFocus, root.getWindow())
        && pipeline.returnFeedback(
            eventId, Feedback.focus(nodeToRestoreFocus, FOCUS_ACTION_INFO_RESTORED));
  } finally {
    AccessibilityNodeInfoUtils.recycleNodes(nodeToRestoreFocus);
  }
}
 
Example 11
Source File: AccessibilityUtil.java    From screenshot-tests-for-android with Apache License 2.0 4 votes vote down vote up
/**
 * Determines if the supplied {@link View} and {@link AccessibilityNodeInfoCompat} has any
 * children which are not independently accessibility focusable and also have a spoken
 * description.
 *
 * <p>NOTE: Accessibility services will include these children's descriptions in the closest
 * focusable ancestor.
 *
 * @param view The {@link View} to evaluate
 * @param node The {@link AccessibilityNodeInfoCompat} to evaluate
 * @return {@code true} if it has any non-actionable speaking descendants within its subtree
 */
public static boolean hasNonActionableSpeakingDescendants(
    @Nullable AccessibilityNodeInfoCompat node, @Nullable View view) {

  if (node == null || view == null || !(view instanceof ViewGroup)) {
    return false;
  }

  final ViewGroup viewGroup = (ViewGroup) view;
  for (int i = 0, count = viewGroup.getChildCount(); i < count; i++) {
    final View childView = viewGroup.getChildAt(i);

    if (childView == null) {
      continue;
    }

    final AccessibilityNodeInfoCompat childNode = createNodeInfoFromView(childView);
    try {
      if (childNode == null) {
        continue;
      }

      if (!childNode.isVisibleToUser()) {
        continue;
      }

      if (isAccessibilityFocusable(childNode, childView)) {
        continue;
      }

      if (isSpeakingNode(childNode, childView)) {
        return true;
      }
    } finally {
      if (childNode != null) {
        childNode.recycle();
      }
    }
  }

  return false;
}
 
Example 12
Source File: AccessibilityUtil.java    From screenshot-tests-for-android with Apache License 2.0 4 votes vote down vote up
/**
 * Returns whether a given {@link View} will be focusable by Google's TalkBack screen reader.
 *
 * @param view The {@link View} to evaluate.
 * @return {@code boolean} if the view will be ignored by TalkBack.
 */
public static boolean isTalkbackFocusable(View view) {
  if (view == null) {
    return false;
  }

  final int important = ViewCompat.getImportantForAccessibility(view);
  if (important == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO
      || important == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
    return false;
  }

  // Go all the way up the tree to make sure no parent has hidden its descendants
  ViewParent parent = view.getParent();
  while (parent instanceof View) {
    if (ViewCompat.getImportantForAccessibility((View) parent)
        == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
      return false;
    }
    parent = parent.getParent();
  }

  // Trying to evaluate the focusability of certain element types (mainly list views) can cause
  // problems when trying to determine the offset of the elements Rect relative to its parent in
  // ViewGroup.offsetRectBetweenParentAndChild. If this happens, simply return false, as this view
  // will not be focusable.
  AccessibilityNodeInfoCompat node;
  try {
    node = createNodeInfoFromView(view);
  } catch (IllegalArgumentException e) {
    return false;
  }

  if (node == null) {
    return false;
  }

  // Non-leaf nodes identical in size to their Window should not be focusable.
  if (areBoundsIdenticalToWindow(node, view) && node.getChildCount() > 0) {
    return false;
  }

  try {
    if (!node.isVisibleToUser()) {
      return false;
    }

    if (isAccessibilityFocusable(node, view)) {
      if (!hasVisibleChildren(view)) {
        // Leaves that are accessibility focusable are never ignored, even if they don't have a
        // speakable description
        return true;
      } else if (isSpeakingNode(node, view)) {
        // Node is focusable and has something to speak
        return true;
      }

      // Node is focusable and has nothing to speak
      return false;
    }

    // if view is not accessibility focusable, it needs to have text and no focusable ancestors.
    if (!hasText(node)) {
      return false;
    }

    if (!hasFocusableAncestor(node, view)) {
      return true;
    }

    return false;
  } finally {
    node.recycle();
  }
}
 
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: AccessibilityNodeInfoUtils.java    From talkback with Apache License 2.0 2 votes vote down vote up
/**
 * Helper method that returns {@code true} if the specified node is visible
 * to the user or if the current SDK doesn't support checking visibility.
 */
public static  boolean isVisibleOrLegacy(AccessibilityNodeInfoCompat node) {
    return (!AccessibilityNodeInfoUtils.SUPPORTS_VISIBILITY || node.isVisibleToUser());
}