Java Code Examples for android.support.v4.view.accessibility.AccessibilityNodeInfoCompat#getChildCount()

The following examples show how to use android.support.v4.view.accessibility.AccessibilityNodeInfoCompat#getChildCount() . 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 stetho with MIT License 6 votes vote down vote up
/**
 * Returns whether the supplied {@link View} and {@link AccessibilityNodeInfoCompat} would
 * produce spoken feedback if it were accessibility focused.  NOTE: not all speaking nodes are
 * focusable.
 *
 * @param view The {@link View} to evaluate
 * @param node The {@link AccessibilityNodeInfoCompat} to evaluate
 * @return {@code true} if it meets the criterion for producing spoken feedback
 */
public static boolean isSpeakingNode(
    @Nullable AccessibilityNodeInfoCompat node,
    @Nullable View view) {
  if (node == null || view == null) {
    return false;
  }

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

  int important = ViewCompat.getImportantForAccessibility(view);
  if (important == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS ||
      (important == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO &&
          node.getChildCount() <= 0)) {
    return false;
  }

  return node.isCheckable() || hasText(node) || hasNonActionableSpeakingDescendants(node, view);
}
 
Example 2
Source File: FocusFinder.java    From brailleback with Apache License 2.0 6 votes vote down vote up
public static AccessibilityNodeInfoCompat findLastFocusableDescendant(
        AccessibilityNodeInfoCompat root, Context context) {
    // null guard and shortcut for leaf nodes.
    if (root == null || root.getChildCount() <= 0) {
        return null;
    }
    HashSet<AccessibilityNodeInfoCompat> seenNodes =
            new HashSet<AccessibilityNodeInfoCompat>();
    seenNodes.add(root);
    try {
        return findLastFocusableDescendantInternal(
                root, context, seenNodes);
    } finally {
        seenNodes.remove(root);  // Not owned by us.
        AccessibilityNodeInfoUtils.recycleNodes(seenNodes);
    }
}
 
Example 3
Source File: AccessibilityNodeInfoUtils.java    From brailleback with Apache License 2.0 6 votes vote down vote up
private static AccessibilityNodeInfoCompat refreshFromChild(
        AccessibilityNodeInfoCompat node) {
    if (node.getChildCount() > 0) {
        AccessibilityNodeInfoCompat firstChild = node.getChild(0);
        if (firstChild != null) {
            AccessibilityNodeInfoCompat parent = firstChild.getParent();
            firstChild.recycle();
            if (node.equals(parent)) {
                return parent;
            } else {
                recycleNodes(parent);
            }
        }
    }
    return null;
}
 
Example 4
Source File: AccessibilityNodeInfoUtils.java    From brailleback with Apache License 2.0 6 votes vote down vote up
private static AccessibilityNodeInfoCompat refreshFromParent(
        AccessibilityNodeInfoCompat node) {
    AccessibilityNodeInfoCompat parent = node.getParent();
    if (parent != null) {
        try {
            int childCount = parent.getChildCount();
            for (int i = 0; i < childCount; ++i) {
                AccessibilityNodeInfoCompat child = parent.getChild(i);
                if (node.equals(child)) {
                    return child;
                }
                recycleNodes(child);
            }
        } finally {
            parent.recycle();
        }
    }
    return null;
}
 
Example 5
Source File: VerticalContainerBrailleRule.java    From brailleback with Apache License 2.0 6 votes vote down vote up
@Override
public void format(Editable result,
        Context context,
        AccessibilityNodeInfoCompat node) {
    boolean empty = (node.getChildCount() == 0);
    int res;
    if (AccessibilityNodeInfoUtils.nodeMatchesClassByType(context, node,
                    GridView.class)) {
        res = empty ? R.string.type_emptygridview : R.string.type_gridview;
    } else if (AccessibilityNodeInfoUtils.nodeMatchesClassByType(
        context, node, ScrollView.class)) {
        res = empty ? R.string.type_emptyscrollview
                : R.string.type_scrollview;
    } else {
        res = empty ? R.string.type_emptylistview : R.string.type_listview;
    }
    result.append(context.getString(res));
}
 
Example 6
Source File: FocusFinder.java    From brailleback with Apache License 2.0 6 votes vote down vote up
public static AccessibilityNodeInfoCompat findFirstFocusableDescendant(
        AccessibilityNodeInfoCompat root, Context context) {
    // null guard and shortcut for leaf nodes.
    if (root == null || root.getChildCount() <= 0) {
        return null;
    }
    HashSet<AccessibilityNodeInfoCompat> seenNodes =
            new HashSet<AccessibilityNodeInfoCompat>();
    seenNodes.add(root);
    try {
        return findFirstFocusableDescendantInternal(
                root, context, seenNodes);
    } finally {
        seenNodes.remove(root);  // Not owned by us.
        AccessibilityNodeInfoUtils.recycleNodes(seenNodes);
    }
}
 
Example 7
Source File: AccessibilityNodeInfoRef.java    From brailleback with Apache License 2.0 5 votes vote down vote up
/**
 * Traverses to the next sibling of this node within its parent, returning
 * {@code true} on success.
 */
public boolean nextSibling() {
    if (mNode == null) {
        return false;
    }
    AccessibilityNodeInfoCompat parent = mNode.getParent();
    if (parent == null) {
        return false;
    }
    try {
        int childCount = parent.getChildCount();
        int childNumber = getChildNumber(parent);
        if (childNumber < 0) {
            return false;
        }
        for (int i = childNumber + 1; i < childCount; ++i) {
            AccessibilityNodeInfoCompat newNode =
                    parent.getChild(i);
            if (newNode == null) {
                return false;
            }
            if (AccessibilityNodeInfoUtils.isVisibleOrLegacy(newNode)) {
                reset(newNode);
                return true;
            }
            newNode.recycle();
        }
    } finally {
        parent.recycle();
    }
    return false;
}
 
Example 8
Source File: FocusFinder.java    From brailleback with Apache License 2.0 5 votes vote down vote up
private static AccessibilityNodeInfoCompat
      findLastFocusableDescendantInternal(
              AccessibilityNodeInfoCompat root, Context context,
              HashSet<AccessibilityNodeInfoCompat> seenNodes) {
    for (int end = root.getChildCount(), i = end - 1; i >= 0; --i) {
        AccessibilityNodeInfoCompat child = root.getChild(i);
        if (child == null) {
            continue;
        }

        AccessibilityNodeInfoCompat n =
                findLastFocusableDescendantInternal(
                        child, context, seenNodes);
        if (n != null) {
            return n;
        }

        if (AccessibilityNodeInfoUtils.shouldFocusNode(context, child)) {
            return child;
        }
        if (!seenNodes.add(child)) {
            LogUtils.log(FocusFinder.class, Log.ERROR,
                    "Cycle in node tree");
            child.recycle();
            return null;
        }
    }
    return null;
}
 
Example 9
Source File: FocusFinder.java    From brailleback with Apache License 2.0 5 votes vote down vote up
private static AccessibilityNodeInfoCompat
      findFirstFocusableDescendantInternal(
              AccessibilityNodeInfoCompat root, Context context,
              HashSet<AccessibilityNodeInfoCompat> seenNodes) {
    for (int i = 0, end = root.getChildCount(); i < end; ++i) {
        AccessibilityNodeInfoCompat child = root.getChild(i);
        if (child == null) {
            continue;
        }
        if (AccessibilityNodeInfoUtils.shouldFocusNode(
                        context, child)) {
            return child;
        }
        if (!seenNodes.add(child)) {
            LogUtils.log(FocusFinder.class, Log.ERROR,
                    "Cycle in node tree");
            child.recycle();
            return null;
        }
        AccessibilityNodeInfoCompat n =
                findFirstFocusableDescendantInternal(
                        child, context, seenNodes);
        if (n != null) {
            return n;
        }
    }
    return null;
}
 
Example 10
Source File: NodeBrailler.java    From brailleback with Apache License 2.0 5 votes vote down vote up
/**
 * Formats {@code node} and its descendants, appending the result
 * to {@code sb}.
 */
private void formatSubtree(AccessibilityNodeInfoCompat node,
        Editable result) {
    if (!node.isVisibleToUser()) {
        return;
    }

    BrailleRule rule = mRuleRepository.find(node);
    SpannableStringBuilder subtreeResult = new SpannableStringBuilder();
    rule.format(subtreeResult, mContext, node);
    if (rule.includeChildren(node, mContext)) {
        int childCount = node.getChildCount();
        for (int i = 0; i < childCount; ++i) {
            AccessibilityNodeInfoCompat child = node.getChild(i);
            if (child == null) {
                continue;
            }
            formatSubtree(child, subtreeResult);
            child.recycle();
        }
    }
    if (!TextUtils.isEmpty(subtreeResult)) {
        // If the node is accessibility focused, add the focus span
        // here to cover the node and its formatted children.
        // This is a fallback in case the formatting rule hasn't set
        // focus by itself.
        if (node.isAccessibilityFocused()
                && subtreeResult.getSpans(0, subtreeResult.length(),
                        DisplaySpans.FocusSpan.class).length == 0) {
            DisplaySpans.addFocus(subtreeResult, 0,
                    subtreeResult.length());
        }
        addNodeSpanForUncovered(node, subtreeResult);
        StringUtils.appendWithSpaces(result, subtreeResult);
    }
}
 
Example 11
Source File: WebInterfaceUtils.java    From brailleback with Apache License 2.0 5 votes vote down vote up
/**
 * Determines whether or not the given node contains ChromeVox content.
 *
 * @param node The node to evaluate
 * @return {@code true} if the node contains ChromeVox content, {@code false} otherwise
 */
public static boolean hasLegacyWebContent(AccessibilityNodeInfoCompat node) {
    if (node == null) {
        return false;
    }

    if (!supportsWebActions(node)) {
        return false;
    }

    // ChromeVox does not have sub elements, so if the parent element also has web content
    // this cannot be ChromeVox.
    AccessibilityNodeInfoCompat parent = node.getParent();
    if (supportsWebActions(parent)) {
        if (parent != null) {
            parent.recycle();
        }

        return false;
    }

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

    // ChromeVox never has child elements
    return node.getChildCount() == 0;
}
 
Example 12
Source File: WebInterfaceUtils.java    From brailleback with Apache License 2.0 5 votes vote down vote up
/**
 * Determines whether or not the given node contains native web content (and not ChromeVox).
 *
 * @param node The node to evaluate
 * @return {@code true} if the node contains native web content, {@code false} otherwise
 */
public static boolean hasNativeWebContent(AccessibilityNodeInfoCompat node) {
    if (node == null) {
        return false;
    }

    if (!supportsWebActions(node)) {
        return false;
    }

    // ChromeVox does not have sub elements, so if the parent element also has web content
    // this cannot be ChromeVox.
    AccessibilityNodeInfoCompat parent = node.getParent();
    if (supportsWebActions(parent)) {
        if (parent != null) {
            parent.recycle();
        }
        return true;
    }

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

    // ChromeVox never has child elements
    return node.getChildCount() > 0;
}
 
Example 13
Source File: AccessibilityNodeInfoRef.java    From brailleback with Apache License 2.0 5 votes vote down vote up
private int getChildNumber(AccessibilityNodeInfoCompat parent) {
    int ret = -1;
    int childCount = parent.getChildCount();
    for (int i = 0; i < childCount && ret < 0; ++i) {
        AccessibilityNodeInfoCompat child = parent.getChild(i);
        if (mNode.equals(child)) {
            ret = i;
        }
        if (child != null) {
            child.recycle();
        }
    }
    return ret;
}
 
Example 14
Source File: AccessibilityNodeInfoUtils.java    From brailleback with Apache License 2.0 5 votes vote down vote up
/**
 * Returns the result of applying a filter using breadth-first traversal.
 *
 * @param context The parent context.
 * @param node The root node to traverse from.
 * @param filter The filter to satisfy.
 * @param maxResults The number of results to stop searching after
 * @return Returns all nodes reached via BFS traversal that satisfies the
 *         filter.
 */
public static List<AccessibilityNodeInfoCompat> searchAllFromBfs(Context context,
        AccessibilityNodeInfoCompat node, NodeFilter filter) {
    if (node == null) {
        return null;
    }

    final List<AccessibilityNodeInfoCompat> toReturn =
            new ArrayList<AccessibilityNodeInfoCompat>();
    final LinkedList<AccessibilityNodeInfoCompat> queue =
            new LinkedList<AccessibilityNodeInfoCompat>();

    queue.add(AccessibilityNodeInfoCompat.obtain(node));

    while (!queue.isEmpty()) {
        final AccessibilityNodeInfoCompat item = queue.removeFirst();

        if (filter.accept(context, item)) {
            toReturn.add(AccessibilityNodeInfoCompat.obtain(item));
        }

        final int childCount = item.getChildCount();

        for (int i = 0; i < childCount; i++) {
            final AccessibilityNodeInfoCompat child = item.getChild(i);

            if (child != null) {
                queue.addLast(child);
            }
        }
    }

    return toReturn;
}
 
Example 15
Source File: AccessibilityNodeInfoUtils.java    From brailleback with Apache License 2.0 5 votes vote down vote up
/**
 * Returns the result of applying a filter using breadth-first traversal.
 *
 * @param context The parent context.
 * @param node The root node to traverse from.
 * @param filter The filter to satisfy.
 * @return The first node reached via BFS traversal that satisfies the
 *         filter.
 */
public static AccessibilityNodeInfoCompat searchFromBfs(
        Context context, AccessibilityNodeInfoCompat node, NodeFilter filter) {
    if (node == null) {
        return null;
    }

    final LinkedList<AccessibilityNodeInfoCompat> queue =
            new LinkedList<AccessibilityNodeInfoCompat>();

    queue.add(AccessibilityNodeInfoCompat.obtain(node));

    while (!queue.isEmpty()) {
        final AccessibilityNodeInfoCompat item = queue.removeFirst();

        if (filter.accept(context, item)) {
            return AccessibilityNodeInfoCompat.obtain(item);
        }

        final int childCount = item.getChildCount();

        for (int i = 0; i < childCount; i++) {
            final AccessibilityNodeInfoCompat child = item.getChild(i);

            if (child != null) {
                queue.addLast(child);
            }
        }
    }

    return null;
}
 
Example 16
Source File: AccessibilityNodeInfoWrapper.java    From stetho with MIT License 4 votes vote down vote up
public static boolean getIgnored(View view) {
  int important = ViewCompat.getImportantForAccessibility(view);
  if (important == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO ||
      important == ViewCompat.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS) {
    return true;
  }

  // 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 true;
    }
    parent = parent.getParent();
  }

  AccessibilityNodeInfoCompat node = createNodeInfoFromView(view);
  try {
    if (!node.isVisibleToUser()) {
      return true;
    }

    if (AccessibilityUtil.isAccessibilityFocusable(node, view)) {
      if (node.getChildCount() <= 0) {
        // Leaves that are accessibility focusable are never ignored, even if they don't have a
        // speakable description
        return false;
      } else if (AccessibilityUtil.isSpeakingNode(node, view)) {
        // Node is focusable and has something to speak
        return false;
      }

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

    // If this node has no focusable ancestors, but it still has text,
    // then it should receive focus from navigation and be read aloud.
    if (!AccessibilityUtil.hasFocusableAncestor(node, view) && AccessibilityUtil.hasText(node)) {
      return false;
    }

    return true;
  } finally {
    node.recycle();
  }
}
 
Example 17
Source File: TimeCatMonitorService.java    From timecat with Apache License 2.0 4 votes vote down vote up
private ArrayList<CopyNode> traverseNode(AccessibilityNodeInfoCompat nodeInfo, int width, int height) {
    ArrayList<CopyNode> nodeList = new ArrayList();
    if (nodeInfo != null && nodeInfo.getInfo() != null) {
        nodeInfo.refresh();

        for (int i = 0; i < nodeInfo.getChildCount(); ++i) {
            //递归遍历nodeInfo
            nodeList.addAll(traverseNode(nodeInfo.getChild(i), width, height));
        }

        if (nodeInfo.getClassName() != null && nodeInfo.getClassName().equals("android.webkit.WebView")) {
            return nodeList;
        } else {
            String content = null;
            String description = content;
            if (nodeInfo.getContentDescription() != null) {
                description = content;
                if (!"".equals(nodeInfo.getContentDescription())) {
                    description = nodeInfo.getContentDescription().toString();
                }
            }

            content = description;
            if (nodeInfo.getText() != null) {
                content = description;
                if (!"".equals(nodeInfo.getText())) {
                    content = nodeInfo.getText().toString();
                }
            }

            if (content != null) {
                Rect outBounds = new Rect();
                nodeInfo.getBoundsInScreen(outBounds);
                if (checkBound(outBounds, width, height)) {
                    nodeList.add(new CopyNode(outBounds, content));
                }
            }

            return nodeList;
        }
    } else {
        return nodeList;
    }
}
 
Example 18
Source File: AccessibilityNodeInfoWrapper.java    From stetho with MIT License 4 votes vote down vote up
@Nullable
public static String getFocusableReasons(View view) {
  AccessibilityNodeInfoCompat node = createNodeInfoFromView(view);
  try {
    boolean hasText = AccessibilityUtil.hasText(node);
    boolean isCheckable = node.isCheckable();
    boolean hasNonActionableSpeakingDescendants =
        AccessibilityUtil.hasNonActionableSpeakingDescendants(node, view);

    if (AccessibilityUtil.isActionableForAccessibility(node)) {
      if (node.getChildCount() <= 0) {
        return "View is actionable and has no children.";
      } else if (hasText) {
        return "View is actionable and has a description.";
      } else if (isCheckable) {
        return "View is actionable and checkable.";
      } else if (hasNonActionableSpeakingDescendants) {
        return "View is actionable and has non-actionable descendants with descriptions.";
      }
    }

    if (AccessibilityUtil.isTopLevelScrollItem(node, view)) {
      if (hasText) {
        return "View is a direct child of a scrollable container and has a description.";
      } else if (isCheckable) {
        return "View is a direct child of a scrollable container and is checkable.";
      } else if (hasNonActionableSpeakingDescendants) {
        return
            "View is a direct child of a scrollable container and has non-actionable " +
            "descendants with descriptions.";
      }
    }

    if (hasText) {
      return "View has a description and is not actionable, but has no actionable ancestor.";
    }

    return null;
  } finally {
    node.recycle();
  }
}
 
Example 19
Source File: AccessibilityNodeInfoUtils.java    From brailleback with Apache License 2.0 4 votes vote down vote up
private static boolean hasNonActionableSpeakingChildren(
        Context context, AccessibilityNodeInfoCompat node) {
    final int childCount = node.getChildCount();

    AccessibilityNodeInfoCompat child = null;

    // Has non-actionable, speaking children?
    for (int i = 0; i < childCount; i++) {
        try {
            child = node.getChild(i);

            if (child == null) {
                LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                        "Child %d is null, skipping it", i);
                continue;
            }

            // Ignore invisible nodes.
            if (!isVisibleOrLegacy(child)) {
                LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                        "Child %d is invisible, skipping it", i);
                continue;
            }

            // Ignore focusable nodes.
            if (FILTER_ACCESSIBILITY_FOCUSABLE.accept(context, child)) {
                LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                        "Child %d is focusable, skipping it", i);
                continue;
            }

            // Recursively check non-focusable child nodes.
            // TODO: Mutual recursion is probably not a good idea.
            if (isSpeakingNode(context, child)) {
                LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                        "Does have actionable speaking children (child %d)", i);
                return true;
            }
        } finally {
            AccessibilityNodeInfoUtils.recycleNodes(child);
        }
    }

    LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
            "Does not have non-actionable speaking children");
    return false;
}
 
Example 20
Source File: AccessibilityNodeInfoUtils.java    From brailleback with Apache License 2.0 4 votes vote down vote up
/**
 * Returns whether a node should receive accessibility focus from
 * navigation. This method should never be called recursively, since it
 * traverses up the parent hierarchy on every call.
 *
 * @see #findFocusFromHover(Context, AccessibilityNodeInfoCompat)
 */
public static boolean shouldFocusNode(Context context, AccessibilityNodeInfoCompat node) {
    if (node == null) {
        return false;
    }

    if (!isVisibleOrLegacy(node)) {
        LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                "Don't focus, node is not visible");
        return false;
    }

    if (FILTER_ACCESSIBILITY_FOCUSABLE.accept(context, node)) {
        // TODO: This may still result in focusing non-speaking nodes, but it
        // won't prevent unlabeled buttons from receiving focus.
        if (node.getChildCount() <= 0) {
            LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                    "Focus, node is focusable and has no children");
            return true;
        } else if (isSpeakingNode(context, node)) {
            LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                    "Focus, node is focusable and has something to speak");
            return true;
        } else {
            LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                    "Don't focus, node is focusable but has nothing to speak");
            return false;
        }
    }

    // If this node has no focusable ancestors, but it still has text,
    // then it should receive focus from navigation and be read aloud.
    if (!hasMatchingAncestor(context, node, FILTER_ACCESSIBILITY_FOCUSABLE)
            && hasText(node)) {
        LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
                "Focus, node has text and no focusable ancestors");
        return true;
    }

    LogUtils.log(AccessibilityNodeInfoUtils.class, Log.VERBOSE,
            "Don't focus, failed all focusability tests");
    return false;
}