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

The following examples show how to use android.support.v4.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 brailleback 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 CharSequence getNodeText(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: AutomationUtils.java    From brailleback with Apache License 2.0 6 votes vote down vote up
/**
 * Returns whether a node matches the class specified by
 * {@code className} and exactly match the text or content description
 * specified by {@code text}.
 */
private static boolean nodeMatchesFilter(Context context, AccessibilityNodeInfoCompat node,
        CharSequence referenceClassName, String findText) {
    final ClassLoadingManager loader = ClassLoadingManager.getInstance();
    final CharSequence nodeClass = node.getClassName();
    final CharSequence nodePackage = node.getPackageName();

    if (!loader.checkInstanceOf(context, nodeClass, nodePackage, referenceClassName)) {
        return false;
    }

    final CharSequence nodeText = node.getText();
    if (TextUtils.equals(findText, nodeText)) {
        return true;
    }

    final CharSequence nodeDesc = node.getContentDescription();
    if (TextUtils.equals(findText, nodeDesc)) {
        return true;
    }

    return false;
}
 
Example 3
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 4
Source File: ExploreByTouchHelper.java    From letv with Apache License 2.0 4 votes vote down vote up
private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
    AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();
    node.setEnabled(true);
    node.setClassName(DEFAULT_CLASS_NAME);
    onPopulateNodeForVirtualView(virtualViewId, node);
    if (node.getText() == null && node.getContentDescription() == null) {
        throw new RuntimeException("Callbacks must add text or a content description in populateNodeForVirtualViewId()");
    }
    node.getBoundsInParent(this.mTempParentRect);
    if (this.mTempParentRect.isEmpty()) {
        throw new RuntimeException("Callbacks must set parent bounds in populateNodeForVirtualViewId()");
    }
    int actions = node.getActions();
    if ((actions & 64) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in populateNodeForVirtualViewId()");
    } else if ((actions & 128) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in populateNodeForVirtualViewId()");
    } else {
        node.setPackageName(this.mView.getContext().getPackageName());
        node.setSource(this.mView, virtualViewId);
        node.setParent(this.mView);
        if (this.mFocusedVirtualViewId == virtualViewId) {
            node.setAccessibilityFocused(true);
            node.addAction(128);
        } else {
            node.setAccessibilityFocused(false);
            node.addAction(64);
        }
        if (intersectVisibleToUser(this.mTempParentRect)) {
            node.setVisibleToUser(true);
            node.setBoundsInParent(this.mTempParentRect);
        }
        this.mView.getLocationOnScreen(this.mTempGlobalRect);
        int offsetX = this.mTempGlobalRect[0];
        int offsetY = this.mTempGlobalRect[1];
        this.mTempScreenRect.set(this.mTempParentRect);
        this.mTempScreenRect.offset(offsetX, offsetY);
        node.setBoundsInScreen(this.mTempScreenRect);
        return node;
    }
}
 
Example 5
Source File: TreeDebug.java    From brailleback with Apache License 2.0 4 votes vote down vote up
/**
 * Gets a description of the properties of a node.
 */
public static CharSequence nodeDebugDescription(AccessibilityNodeInfoCompat node) {
    StringBuilder sb = new StringBuilder();
    sb.append(node.getWindowId());

    if (node.getClassName() != null) {
        appendSimpleName(sb, node.getClassName());
    } else {
        sb.append("??");
    }

    if (!node.isVisibleToUser()) {
        sb.append(":invisible");
    }

    if (node.getText() != null) {
        sb.append(":");
        sb.append(node.getText().toString().trim());
    }

    if (node.getContentDescription() != null) {
        sb.append(":");
        sb.append(node.getContentDescription().toString().trim());
    }

    int actions = node.getActions();
    if (actions != 0) {
        sb.append(":");
        if ((actions & AccessibilityNodeInfoCompat.ACTION_FOCUS) != 0) {
            sb.append("F");
        }
        if ((actions & AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS) != 0) {
            sb.append("A");
        }
        if ((actions & AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
            sb.append("a");
        }
        if ((actions & AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD) != 0) {
            sb.append("-");
        }
        if ((actions & AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD) != 0) {
            sb.append("+");
        }
    }

    if (node.isCheckable()) {
        sb.append(":");
        if (node.isChecked()) {
            sb.append("(X)");
        } else {
            sb.append("( )");
        }
    }

    if (node.isFocusable()) {
        sb.append(":focusable");
    }

    if (node.isFocused()) {
        sb.append(":focused");
    }

    if (node.isSelected()) {
        sb.append(":selected");
    }

    if (node.isClickable()) {
        sb.append(":clickable");
    }

    if (node.isLongClickable()) {
        sb.append(":longClickable");
    }

    if (node.isAccessibilityFocused()) {
        sb.append(":accessibilityFocused");
    }

    if (!node.isEnabled()) {
        sb.append(":disabled");
    }

    return sb.toString();
}
 
Example 6
Source File: AccessibilityNodeInfoWrapper.java    From stetho with MIT License 4 votes vote down vote up
@Nullable
public static CharSequence getDescription(View view) {
  AccessibilityNodeInfoCompat node = createNodeInfoFromView(view);
  try {
    CharSequence contentDescription = node.getContentDescription();
    CharSequence nodeText = node.getText();

    boolean hasNodeText = !TextUtils.isEmpty(nodeText);
    boolean isEditText = view instanceof EditText;

    // EditText's prioritize their own text content over a contentDescription
    if (!TextUtils.isEmpty(contentDescription) && (!isEditText || !hasNodeText)) {
      return contentDescription;
    }

    if (hasNodeText) {
      return nodeText;
    }

    // If there are child views and no contentDescription the text of all non-focusable children,
    // comma separated, becomes the description.
    if (view instanceof ViewGroup) {
      final StringBuilder concatChildDescription = new StringBuilder();
      final String separator = ", ";
      ViewGroup viewGroup = (ViewGroup) view;

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

        AccessibilityNodeInfoCompat childNodeInfo = AccessibilityNodeInfoCompat.obtain();
        ViewCompat.onInitializeAccessibilityNodeInfo(child, childNodeInfo);

        CharSequence childNodeDescription = null;
        if (AccessibilityUtil.isSpeakingNode(childNodeInfo, child) &&
            !AccessibilityUtil.isAccessibilityFocusable(childNodeInfo, child)) {
          childNodeDescription = getDescription(child);
        }

        if (!TextUtils.isEmpty(childNodeDescription)) {
          if (concatChildDescription.length() > 0) {
            concatChildDescription.append(separator);
          }
          concatChildDescription.append(childNodeDescription);
        }
        childNodeInfo.recycle();
      }

      return concatChildDescription.length() > 0 ? concatChildDescription.toString() : null;
    }

    return null;
  } finally {
    node.recycle();
  }
}
 
Example 7
Source File: ExploreByTouchHelper.java    From MiBandDecompiled with Apache License 2.0 4 votes vote down vote up
private AccessibilityNodeInfoCompat d(int l)
{
    AccessibilityNodeInfoCompat accessibilitynodeinfocompat = AccessibilityNodeInfoCompat.obtain();
    accessibilitynodeinfocompat.setEnabled(true);
    accessibilitynodeinfocompat.setClassName(b);
    onPopulateNodeForVirtualView(l, accessibilitynodeinfocompat);
    if (accessibilitynodeinfocompat.getText() == null && accessibilitynodeinfocompat.getContentDescription() == null)
    {
        throw new RuntimeException("Callbacks must add text or a content description in populateNodeForVirtualViewId()");
    }
    accessibilitynodeinfocompat.getBoundsInParent(d);
    if (d.isEmpty())
    {
        throw new RuntimeException("Callbacks must set parent bounds in populateNodeForVirtualViewId()");
    }
    int i1 = accessibilitynodeinfocompat.getActions();
    if ((i1 & 0x40) != 0)
    {
        throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in populateNodeForVirtualViewId()");
    }
    if ((i1 & 0x80) != 0)
    {
        throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in populateNodeForVirtualViewId()");
    }
    accessibilitynodeinfocompat.setPackageName(h.getContext().getPackageName());
    accessibilitynodeinfocompat.setSource(h, l);
    accessibilitynodeinfocompat.setParent(h);
    int j1;
    int k1;
    if (j == l)
    {
        accessibilitynodeinfocompat.setAccessibilityFocused(true);
        accessibilitynodeinfocompat.addAction(128);
    } else
    {
        accessibilitynodeinfocompat.setAccessibilityFocused(false);
        accessibilitynodeinfocompat.addAction(64);
    }
    if (a(d))
    {
        accessibilitynodeinfocompat.setVisibleToUser(true);
        accessibilitynodeinfocompat.setBoundsInParent(d);
    }
    h.getLocationOnScreen(f);
    j1 = f[0];
    k1 = f[1];
    c.set(d);
    c.offset(j1, k1);
    accessibilitynodeinfocompat.setBoundsInScreen(c);
    return accessibilitynodeinfocompat;
}
 
Example 8
Source File: ExploreByTouchHelper.java    From adt-leanback-support with Apache License 2.0 4 votes vote down vote up
/**
 * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
 * specified item. Automatically manages accessibility focus actions.
 * <p>
 * Allows the implementing class to specify most node properties, but
 * overrides the following:
 * <ul>
 * <li>{@link AccessibilityNodeInfoCompat#setPackageName}
 * <li>{@link AccessibilityNodeInfoCompat#setClassName}
 * <li>{@link AccessibilityNodeInfoCompat#setParent(View)}
 * <li>{@link AccessibilityNodeInfoCompat#setSource(View, int)}
 * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
 * </ul>
 * <p>
 * Uses the bounds of the parent view and the parent-relative bounding
 * rectangle specified by
 * {@link AccessibilityNodeInfoCompat#getBoundsInParent} to automatically
 * update the following properties:
 * <ul>
 * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
 * </ul>
 *
 * @param virtualViewId The virtual view id for item for which to construct
 *            a node.
 * @return An {@link AccessibilityNodeInfoCompat} for the specified item.
 */
private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
    final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();

    // Ensure the client has good defaults.
    node.setEnabled(true);
    node.setClassName(DEFAULT_CLASS_NAME);

    // Allow the client to populate the node.
    onPopulateNodeForVirtualView(virtualViewId, node);

    // Make sure the developer is following the rules.
    if ((node.getText() == null) && (node.getContentDescription() == null)) {
        throw new RuntimeException("Callbacks must add text or a content description in "
                + "populateNodeForVirtualViewId()");
    }

    node.getBoundsInParent(mTempParentRect);
    if (mTempParentRect.isEmpty()) {
        throw new RuntimeException("Callbacks must set parent bounds in "
                + "populateNodeForVirtualViewId()");
    }

    final int actions = node.getActions();
    if ((actions & AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
                + "populateNodeForVirtualViewId()");
    }
    if ((actions & AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
                + "populateNodeForVirtualViewId()");
    }

    // Don't allow the client to override these properties.
    node.setPackageName(mView.getContext().getPackageName());
    node.setSource(mView, virtualViewId);
    node.setParent(mView);

    // Manage internal accessibility focus state.
    if (mFocusedVirtualViewId == virtualViewId) {
        node.setAccessibilityFocused(true);
        node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
    } else {
        node.setAccessibilityFocused(false);
        node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
    }

    // Set the visibility based on the parent bound.
    if (intersectVisibleToUser(mTempParentRect)) {
        node.setVisibleToUser(true);
        node.setBoundsInParent(mTempParentRect);
    }

    // Calculate screen-relative bound.
    mView.getLocationOnScreen(mTempGlobalRect);
    final int offsetX = mTempGlobalRect[0];
    final int offsetY = mTempGlobalRect[1];
    mTempScreenRect.set(mTempParentRect);
    mTempScreenRect.offset(offsetX, offsetY);
    node.setBoundsInScreen(mTempScreenRect);

    return node;
}
 
Example 9
Source File: ExploreByTouchHelper.java    From android-recipes-app with Apache License 2.0 4 votes vote down vote up
/**
 * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
 * specified item. Automatically manages accessibility focus actions.
 * <p>
 * Allows the implementing class to specify most node properties, but
 * overrides the following:
 * <ul>
 * <li>{@link AccessibilityNodeInfoCompat#setPackageName}
 * <li>{@link AccessibilityNodeInfoCompat#setClassName}
 * <li>{@link AccessibilityNodeInfoCompat#setParent(View)}
 * <li>{@link AccessibilityNodeInfoCompat#setSource(View, int)}
 * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
 * </ul>
 * <p>
 * Uses the bounds of the parent view and the parent-relative bounding
 * rectangle specified by
 * {@link AccessibilityNodeInfoCompat#getBoundsInParent} to automatically
 * update the following properties:
 * <ul>
 * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
 * </ul>
 *
 * @param virtualViewId The virtual view id for item for which to construct
 *            a node.
 * @return An {@link AccessibilityNodeInfoCompat} for the specified item.
 */
private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
    final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();

    // Ensure the client has good defaults.
    node.setEnabled(true);
    node.setClassName(DEFAULT_CLASS_NAME);

    // Allow the client to populate the node.
    onPopulateNodeForVirtualView(virtualViewId, node);

    // Make sure the developer is following the rules.
    if ((node.getText() == null) && (node.getContentDescription() == null)) {
        throw new RuntimeException("Callbacks must add text or a content description in "
                + "populateNodeForVirtualViewId()");
    }

    node.getBoundsInParent(mTempParentRect);
    if (mTempParentRect.isEmpty()) {
        throw new RuntimeException("Callbacks must set parent bounds in "
                + "populateNodeForVirtualViewId()");
    }

    final int actions = node.getActions();
    if ((actions & AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
                + "populateNodeForVirtualViewId()");
    }
    if ((actions & AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
                + "populateNodeForVirtualViewId()");
    }

    // Don't allow the client to override these properties.
    node.setPackageName(mView.getContext().getPackageName());
    node.setSource(mView, virtualViewId);
    node.setParent(mView);

    // Manage internal accessibility focus state.
    if (mFocusedVirtualViewId == virtualViewId) {
        node.setAccessibilityFocused(true);
        node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
    } else {
        node.setAccessibilityFocused(false);
        node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
    }

    // Set the visibility based on the parent bound.
    if (intersectVisibleToUser(mTempParentRect)) {
        node.setVisibleToUser(true);
        node.setBoundsInParent(mTempParentRect);
    }

    // Calculate screen-relative bound.
    mView.getLocationOnScreen(mTempGlobalRect);
    final int offsetX = mTempGlobalRect[0];
    final int offsetY = mTempGlobalRect[1];
    mTempScreenRect.set(mTempParentRect);
    mTempScreenRect.offset(offsetX, offsetY);
    node.setBoundsInScreen(mTempScreenRect);

    return node;
}
 
Example 10
Source File: ExploreByTouchHelper.java    From V.FlyoutTest with MIT License 4 votes vote down vote up
/**
 * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
 * specified item. Automatically manages accessibility focus actions.
 * <p>
 * Allows the implementing class to specify most node properties, but
 * overrides the following:
 * <ul>
 * <li>{@link AccessibilityNodeInfoCompat#setPackageName}
 * <li>{@link AccessibilityNodeInfoCompat#setClassName}
 * <li>{@link AccessibilityNodeInfoCompat#setParent(View)}
 * <li>{@link AccessibilityNodeInfoCompat#setSource(View, int)}
 * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
 * </ul>
 * <p>
 * Uses the bounds of the parent view and the parent-relative bounding
 * rectangle specified by
 * {@link AccessibilityNodeInfoCompat#getBoundsInParent} to automatically
 * update the following properties:
 * <ul>
 * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
 * </ul>
 *
 * @param virtualViewId The virtual view id for item for which to construct
 *            a node.
 * @return An {@link AccessibilityNodeInfoCompat} for the specified item.
 */
private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
    final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();

    // Ensure the client has good defaults.
    node.setEnabled(true);
    node.setClassName(DEFAULT_CLASS_NAME);

    // Allow the client to populate the node.
    onPopulateNodeForVirtualView(virtualViewId, node);

    // Make sure the developer is following the rules.
    if ((node.getText() == null) && (node.getContentDescription() == null)) {
        throw new RuntimeException("Callbacks must add text or a content description in "
                + "populateNodeForVirtualViewId()");
    }

    node.getBoundsInParent(mTempParentRect);
    if (mTempParentRect.isEmpty()) {
        throw new RuntimeException("Callbacks must set parent bounds in "
                + "populateNodeForVirtualViewId()");
    }

    final int actions = node.getActions();
    if ((actions & AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
                + "populateNodeForVirtualViewId()");
    }
    if ((actions & AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
                + "populateNodeForVirtualViewId()");
    }

    // Don't allow the client to override these properties.
    node.setPackageName(mView.getContext().getPackageName());
    node.setSource(mView, virtualViewId);
    node.setParent(mView);

    // Manage internal accessibility focus state.
    if (mFocusedVirtualViewId == virtualViewId) {
        node.setAccessibilityFocused(true);
        node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
    } else {
        node.setAccessibilityFocused(false);
        node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
    }

    // Set the visibility based on the parent bound.
    if (intersectVisibleToUser(mTempParentRect)) {
        node.setVisibleToUser(true);
        node.setBoundsInParent(mTempParentRect);
    }

    // Calculate screen-relative bound.
    mView.getLocationOnScreen(mTempGlobalRect);
    final int offsetX = mTempGlobalRect[0];
    final int offsetY = mTempGlobalRect[1];
    mTempScreenRect.set(mTempParentRect);
    mTempScreenRect.offset(offsetX, offsetY);
    node.setBoundsInScreen(mTempScreenRect);

    return node;
}
 
Example 11
Source File: ExploreByTouchHelper.java    From guideshow with MIT License 4 votes vote down vote up
/**
 * Constructs and returns an {@link AccessibilityNodeInfoCompat} for the
 * specified item. Automatically manages accessibility focus actions.
 * <p>
 * Allows the implementing class to specify most node properties, but
 * overrides the following:
 * <ul>
 * <li>{@link AccessibilityNodeInfoCompat#setPackageName}
 * <li>{@link AccessibilityNodeInfoCompat#setClassName}
 * <li>{@link AccessibilityNodeInfoCompat#setParent(View)}
 * <li>{@link AccessibilityNodeInfoCompat#setSource(View, int)}
 * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInScreen(Rect)}
 * </ul>
 * <p>
 * Uses the bounds of the parent view and the parent-relative bounding
 * rectangle specified by
 * {@link AccessibilityNodeInfoCompat#getBoundsInParent} to automatically
 * update the following properties:
 * <ul>
 * <li>{@link AccessibilityNodeInfoCompat#setVisibleToUser}
 * <li>{@link AccessibilityNodeInfoCompat#setBoundsInParent}
 * </ul>
 *
 * @param virtualViewId The virtual view id for item for which to construct
 *            a node.
 * @return An {@link AccessibilityNodeInfoCompat} for the specified item.
 */
private AccessibilityNodeInfoCompat createNodeForChild(int virtualViewId) {
    final AccessibilityNodeInfoCompat node = AccessibilityNodeInfoCompat.obtain();

    // Ensure the client has good defaults.
    node.setEnabled(true);
    node.setClassName(DEFAULT_CLASS_NAME);

    // Allow the client to populate the node.
    onPopulateNodeForVirtualView(virtualViewId, node);

    // Make sure the developer is following the rules.
    if ((node.getText() == null) && (node.getContentDescription() == null)) {
        throw new RuntimeException("Callbacks must add text or a content description in "
                + "populateNodeForVirtualViewId()");
    }

    node.getBoundsInParent(mTempParentRect);
    if (mTempParentRect.isEmpty()) {
        throw new RuntimeException("Callbacks must set parent bounds in "
                + "populateNodeForVirtualViewId()");
    }

    final int actions = node.getActions();
    if ((actions & AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_ACCESSIBILITY_FOCUS in "
                + "populateNodeForVirtualViewId()");
    }
    if ((actions & AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS) != 0) {
        throw new RuntimeException("Callbacks must not add ACTION_CLEAR_ACCESSIBILITY_FOCUS in "
                + "populateNodeForVirtualViewId()");
    }

    // Don't allow the client to override these properties.
    node.setPackageName(mView.getContext().getPackageName());
    node.setSource(mView, virtualViewId);
    node.setParent(mView);

    // Manage internal accessibility focus state.
    if (mFocusedVirtualViewId == virtualViewId) {
        node.setAccessibilityFocused(true);
        node.addAction(AccessibilityNodeInfoCompat.ACTION_CLEAR_ACCESSIBILITY_FOCUS);
    } else {
        node.setAccessibilityFocused(false);
        node.addAction(AccessibilityNodeInfoCompat.ACTION_ACCESSIBILITY_FOCUS);
    }

    // Set the visibility based on the parent bound.
    if (intersectVisibleToUser(mTempParentRect)) {
        node.setVisibleToUser(true);
        node.setBoundsInParent(mTempParentRect);
    }

    // Calculate screen-relative bound.
    mView.getLocationOnScreen(mTempGlobalRect);
    final int offsetX = mTempGlobalRect[0];
    final int offsetY = mTempGlobalRect[1];
    mTempScreenRect.set(mTempParentRect);
    mTempScreenRect.offset(offsetX, offsetY);
    node.setBoundsInScreen(mTempScreenRect);

    return node;
}