in core/src/main/java/com/facebook/testing/screenshot/layouthierarchy/AccessibilityUtil.java [505:578]
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();
}
}