下面列出了android.view.accessibility.AccessibilityNodeInfo#getChildCount ( ) 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private void traverse_node_recursive(AccessibilityNodeInfo node, int level) {
if (node == null) {
return;
}
int child_cnt = node.getChildCount();
StringBuilder sb = new StringBuilder();
if (child_cnt > 0) {
for (int i = 0; i < child_cnt; i++) {
traverse_node_recursive(node.getChild(i), level + 1);
}
} else {
Rect bounds = new Rect();
node.getBoundsInScreen(bounds);
for (int i = 0; i < level; i++) {
sb.append(" ");
}
sb.append(node.getText());
sb.append(bounds.toShortString());
Util.log(sb);
}
}
private List<AccessibilityNodeInfo> flattenTree(AccessibilityNodeInfo rootNode) {
List<AccessibilityNodeInfo> allNodes = new ArrayList<AccessibilityNodeInfo>();
Queue<AccessibilityNodeInfo> Q = new LinkedList<AccessibilityNodeInfo>();
Q.add(rootNode);
// BFS, level-order traversal
while (!Q.isEmpty()) {
AccessibilityNodeInfo node = Q.poll();
allNodes.add(node);
for (int i = 0; i < node.getChildCount(); i++) {
AccessibilityNodeInfo child = node.getChild(i);
Q.add(child);
}
}
return allNodes;
}
public static void dumpTree(AccessibilityNodeInfo root, int level) {
AccessibilityNodeInfo content = root;
String s = "";
for (int i = 0; i < level; i++) {
s += " ";
}
Rect bound = new Rect();
root.getBoundsInParent(bound);
Util.log(s + content.getClassName() + "," + content.getText() + "," + content.isVisibleToUser() + "," + bound);
for (int i = 0; i < root.getChildCount(); i++) {
MyAccessibilityNodeInfoSuite.dumpTree(root.getChild(i), level + 1);
}
}
private static void logNodeHierachy(AccessibilityNodeInfo nodeInfo, int depth) {
Rect bounds = new Rect();
nodeInfo.getBoundsInScreen(bounds);
StringBuilder sb = new StringBuilder();
if (depth > 0) {
for (int i=0; i<depth; i++) {
sb.append(" ");
}
sb.append("\u2514 ");
}
sb.append(nodeInfo.getClassName());
sb.append(" (" + nodeInfo.getChildCount() + ")");
sb.append(" " + bounds.toString());
if (nodeInfo.getText() != null) {
sb.append(" - \"" + nodeInfo.getText() + "\"");
}
Log.v(TAG, sb.toString());
for (int i=0; i<nodeInfo.getChildCount(); i++) {
AccessibilityNodeInfo childNode = nodeInfo.getChild(i);
if (childNode != null) {
logNodeHierachy(childNode, depth + 1);
}
}
}
private List<UiAutomationElement> buildChildren(AccessibilityNodeInfo node) {
final int childCount = node.getChildCount();
if (childCount == 0 || getDepth() >= MAX_DEPTH) {
if (getDepth() >= MAX_DEPTH) {
Logger.warn(String.format("Skipping building children of '%s' because the maximum " +
"recursion depth (%s) has been reached", node, MAX_DEPTH));
}
return Collections.emptyList();
}
List<UiAutomationElement> children = new ArrayList<>(childCount);
boolean areInvisibleElementsAllowed = AppiumUIA2Driver
.getInstance()
.getSessionOrThrow()
.getCapability(ALLOW_INVISIBLE_ELEMENTS.toString(), false);
for (int i = 0; i < childCount; i++) {
AccessibilityNodeInfo child = node.getChild(i);
//Ignore if element is not visible on the screen
if (child != null && (child.isVisibleToUser() || areInvisibleElementsAllowed)) {
children.add(getOrCreateElement(child, i, getDepth() + 1));
}
}
return children;
}
private List<AccessibilityNodeInfo> get_leaf_nodes(AccessibilityNodeInfo root) {
List<AccessibilityNodeInfo> ret = new ArrayList<AccessibilityNodeInfo>();
Queue<AccessibilityNodeInfo> Q = new LinkedList<AccessibilityNodeInfo>();
Q.add(root);
while (!Q.isEmpty()) {
AccessibilityNodeInfo node = Q.poll();
if (node == null) {
Util.log("Processing NULL");
continue;
}
int childCnt = node.getChildCount();
if (childCnt > 0) {
for (int i = 0; i < childCnt; i++) {
AccessibilityNodeInfo child = node.getChild(i);
Q.add(child); // no need to check NULL, checked above
}
} else {
ret.add(node);
}
}
return ret;
}
public static SerializedAccessibilityNodeInfo serialize(AccessibilityNodeInfo node){
SerializedAccessibilityNodeInfo serializedNode = new SerializedAccessibilityNodeInfo();
Rect boundsInScreen = new Rect(), boundsInParent = new Rect();
if(node == null){
return null;
}
if(node.getClassName() != null)
serializedNode.className = node.getClassName().toString();
node.getBoundsInScreen(boundsInScreen);
node.getBoundsInParent(boundsInParent);
serializedNode.boundsInScreen = boundsInScreen.flattenToString();
serializedNode.boundsInParent = boundsInParent.flattenToString();
if(node.getContentDescription() != null)
serializedNode.contentDescription = node.getContentDescription().toString();
if(node.getText() != null){
serializedNode.text = node.getText().toString();
}
if(node.getViewIdResourceName() != null)
serializedNode.viewId = node.getViewIdResourceName();
int childCount = node.getChildCount();
for(int i = 0; i < childCount; i ++){
if(node.getChild(i) != null){
serializedNode.children.add(serialize(node.getChild(i)));
}
}
return serializedNode;
}
private void prefetchSiblingsOfVirtualNode(AccessibilityNodeInfo current, View providerHost,
AccessibilityNodeProvider provider, List<AccessibilityNodeInfo> outInfos) {
final long parentNodeId = current.getParentNodeId();
final int parentAccessibilityViewId =
AccessibilityNodeInfo.getAccessibilityViewId(parentNodeId);
final int parentVirtualDescendantId =
AccessibilityNodeInfo.getVirtualDescendantId(parentNodeId);
if (parentVirtualDescendantId != AccessibilityNodeProvider.HOST_VIEW_ID
|| parentAccessibilityViewId == providerHost.getAccessibilityViewId()) {
final AccessibilityNodeInfo parent =
provider.createAccessibilityNodeInfo(parentVirtualDescendantId);
if (parent != null) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
if (outInfos.size() >= MAX_ACCESSIBILITY_NODE_INFO_BATCH_SIZE) {
return;
}
final long childNodeId = parent.getChildId(i);
if (childNodeId != current.getSourceNodeId()) {
final int childVirtualDescendantId =
AccessibilityNodeInfo.getVirtualDescendantId(childNodeId);
AccessibilityNodeInfo child = provider.createAccessibilityNodeInfo(
childVirtualDescendantId);
if (child != null) {
outInfos.add(child);
}
}
}
}
} else {
prefetchSiblingsOfRealNode(providerHost, outInfos);
}
}
private void findChildView2(AccessibilityNodeInfo info, String parentText) {
parentText = parentText + " |-- " + info.getText();
L.d("得到控件 " + parentText);
for (int i = 0; i < info.getChildCount(); i++) {
AccessibilityNodeInfo child = info.getChild(i);
if (child != null) {
// L.d("得到子控件 " + child.getText());
findChildView2(child, parentText + "");
} else {
// L.d("得到所有控件" + info.getText());
}
}
}
private static void getLeaves(List<AccessibilityNodeInfo> leaves, AccessibilityNodeInfo node) {
if (node.getChildCount() == 0) {
leaves.add(node);
return;
}
for (int i = 0; i < node.getChildCount(); i++) {
getLeaves(leaves, node.getChild(i));
}
}
/**
* 遍历查找红包
*/
private void findRedPacket(AccessibilityNodeInfo rootNode) {
if (rootNode != null) {
//从最后一行开始找起
for (int i = rootNode.getChildCount() - 1; i >= 0; i--) {
AccessibilityNodeInfo node = rootNode.getChild(i);
//如果node为空则跳过该节点
if (node == null) {
continue;
}
CharSequence text = node.getText();
if (text != null && text.toString().equals("领取红包")) {
AccessibilityNodeInfo parent = node.getParent();
//while循环,遍历"领取红包"的各个父布局,直至找到可点击的为止
while (parent != null) {
if (parent.isClickable()) {
//模拟点击
Log.e("spire发现了列表页的红包 执行点击", text.toString());
parent.performAction(AccessibilityNodeInfo.ACTION_CLICK);
//isOpenRP用于判断该红包是否点击过
isOpenRP = true;
break;
}
parent = parent.getParent();
}
}
//判断是否已经打开过那个最新的红包了,是的话就跳出for循环,不是的话继续遍历
if (isOpenRP) {
break;
} else {
findRedPacket(node);
}
}
}
}
private void scrapeCompleteSubtree_MAIN(Tree.TreeNode treeNode, AccessibilityNodeInfo node, PerformNodeAction nodeAction) {
checkHandlerThread();
node.refresh();
checkFocusedNode_PreLollipop(node);
if (!node.isVisibleToUser()) {
return;
}
int cc = node.getChildCount();
for (int i = 0; i < cc; i++) {
AccessibilityNodeInfo child = node.getChild(i);
if (child != null) {
Tree.TreeNode childTreeNode = mTree.put(child);
treeNode.addChild(childTreeNode);
if (nodeAction != null) {
nodeAction.onNodeScanned(child);
}
scrapeCompleteSubtree_MAIN(childTreeNode, child, nodeAction);
child.recycle();
} else {
Ln.d("SKRAPE: warning, couldn't get a child!");
//TODO: one reason for this might be a too large binder transaction -> maybe at least give some feedback to the user
}
}
}
/**
* This should be used when it's already determined that the node is NAF and a further check of
* its children is in order. A node maybe a container such as LinerLayout and may be set to be
* clickable but have no text or content description but it is counting on one of its children
* to fulfill the requirement for being accessibility friendly by having one or more of its
* children fill the text or content-description. Such a combination is considered by this
* dumper as acceptable for accessibility.
*
* @return false if node fails the check.
*/
private static boolean childNafCheck(AccessibilityNodeInfo node) {
int childCount = node.getChildCount();
for (int x = 0; x < childCount; x++) {
AccessibilityNodeInfo childNode = node.getChild(x);
if (childNode == null) {
Log.i(String.format("Null child %d/%d, parent: %s", x, childCount, node.toString()));
continue;
}
if (!safeCharSeqToString(childNode.getContentDescription()).isEmpty() || !safeCharSeqToString(childNode.getText()).isEmpty())
return true;
if (childNafCheck(childNode)) return true;
}
return false;
}
/**
* Traverses to the next sibling of this node within its parent, returning
* {@code true} on success.
*/
public boolean nextSibling() {
if (mNode == null) {
return false;
}
AccessibilityNodeInfo 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) {
AccessibilityNodeInfo newNode =
parent.getChild(i);
if (newNode == null) {
return false;
}
if (AccessibilityNodeInfoUtils.isVisibleOrLegacy(newNode)) {
reset(newNode);
return true;
}
newNode.recycle();
}
} finally {
parent.recycle();
}
return false;
}
private AccessibilityNodeInfo findNodeRegularRecursive(
UiSelector subSelector, AccessibilityNodeInfo fromNode, int index) {
if (subSelector.isMatchFor(fromNode, index)) {
if (DEBUG) {
Log.d(LOG_TAG,
formatLog(String.format("%s",
subSelector.dumpToString(false))));
}
if (subSelector.isLeaf()) {
return fromNode;
}
if (subSelector.hasChildSelector()) {
mLogIndent++; // next selector
subSelector = subSelector.getChildSelector();
if (subSelector == null) {
Log.e(LOG_TAG, "Error: A child selector without content");
return null; // there is an implementation fault
}
} else if (subSelector.hasParentSelector()) {
mLogIndent++; // next selector
subSelector = subSelector.getParentSelector();
if (subSelector == null) {
Log.e(LOG_TAG, "Error: A parent selector without content");
return null; // there is an implementation fault
}
// the selector requested we start at this level from
// the parent node from the one we just matched
fromNode = fromNode.getParent();
if (fromNode == null)
return null;
}
}
int childCount = fromNode.getChildCount();
boolean hasNullChild = false;
for (int i = 0; i < childCount; i++) {
AccessibilityNodeInfo childNode = fromNode.getChild(i);
if (childNode == null) {
Log.w(LOG_TAG,
String.format(
"AccessibilityNodeInfo returned a null child (%d of %d)",
i, childCount));
if (!hasNullChild) {
Log.w(LOG_TAG,
String.format("parent = %s", fromNode.toString()));
}
hasNullChild = true;
continue;
}
if (!childNode.isVisibleToUser()) {
if (VERBOSE)
Log.v(LOG_TAG, String.format(
"Skipping invisible child: %s",
childNode.toString()));
continue;
}
AccessibilityNodeInfo retNode = findNodeRegularRecursive(
subSelector, childNode, i);
if (retNode != null) {
return retNode;
}
}
return null;
}
private AccessibilityNodeInfo findNodeRegularRecursive(UiSelector subSelector,
AccessibilityNodeInfo fromNode, int index) {
if (subSelector.isMatchFor(fromNode, index)) {
if (DEBUG) {
Log.d(LOG_TAG, formatLog(String.format("%s",
subSelector.dumpToString(false))));
}
if(subSelector.isLeaf()) {
return fromNode;
}
if(subSelector.hasChildSelector()) {
mLogIndent++; // next selector
subSelector = subSelector.getChildSelector();
if(subSelector == null) {
Log.e(LOG_TAG, "Error: A child selector without content");
return null; // there is an implementation fault
}
} else if(subSelector.hasParentSelector()) {
mLogIndent++; // next selector
subSelector = subSelector.getParentSelector();
if(subSelector == null) {
Log.e(LOG_TAG, "Error: A parent selector without content");
return null; // there is an implementation fault
}
// the selector requested we start at this level from
// the parent node from the one we just matched
fromNode = fromNode.getParent();
if(fromNode == null)
return null;
}
}
int childCount = fromNode.getChildCount();
boolean hasNullChild = false;
for (int i = 0; i < childCount; i++) {
AccessibilityNodeInfo childNode = fromNode.getChild(i);
if (childNode == null) {
Log.w(LOG_TAG, String.format(
"AccessibilityNodeInfo returned a null child (%d of %d)", i, childCount));
if (!hasNullChild) {
Log.w(LOG_TAG, String.format("parent = %s", fromNode.toString()));
}
hasNullChild = true;
continue;
}
if (!childNode.isVisibleToUser()) {
if (VERBOSE)
Log.v(LOG_TAG,
String.format("Skipping invisible child: %s", childNode.toString()));
continue;
}
AccessibilityNodeInfo retNode = findNodeRegularRecursive(subSelector, childNode, i);
if (retNode != null) {
return retNode;
}
}
return null;
}
private AccessibilityNodeInfo findNodePatternRecursive(
UiSelector subSelector, AccessibilityNodeInfo fromNode, int index,
UiSelector originalPattern) {
if (subSelector.isMatchFor(fromNode, index)) {
if(subSelector.isLeaf()) {
if(mPatternIndexer == 0) {
if (DEBUG)
Log.d(LOG_TAG, formatLog(
String.format("%s", subSelector.dumpToString(false))));
return fromNode;
} else {
if (DEBUG)
Log.d(LOG_TAG, formatLog(
String.format("%s", subSelector.dumpToString(false))));
mPatternCounter++; //count the pattern matched
mPatternIndexer--; //decrement until zero for the instance requested
// At a leaf selector within a group and still not instance matched
// then reset the selector to continue search from current position
// in the accessibility tree for the next pattern match up until the
// pattern index hits 0.
subSelector = originalPattern;
// starting over with next pattern search so reset to parent level
mLogIndent = mLogParentIndent;
}
} else {
if (DEBUG)
Log.d(LOG_TAG, formatLog(
String.format("%s", subSelector.dumpToString(false))));
if(subSelector.hasChildSelector()) {
mLogIndent++; // next selector
subSelector = subSelector.getChildSelector();
if(subSelector == null) {
Log.e(LOG_TAG, "Error: A child selector without content");
return null;
}
} else if(subSelector.hasParentSelector()) {
mLogIndent++; // next selector
subSelector = subSelector.getParentSelector();
if(subSelector == null) {
Log.e(LOG_TAG, "Error: A parent selector without content");
return null;
}
fromNode = fromNode.getParent();
if(fromNode == null)
return null;
}
}
}
int childCount = fromNode.getChildCount();
boolean hasNullChild = false;
for (int i = 0; i < childCount; i++) {
AccessibilityNodeInfo childNode = fromNode.getChild(i);
if (childNode == null) {
Log.w(LOG_TAG, String.format(
"AccessibilityNodeInfo returned a null child (%d of %d)", i, childCount));
if (!hasNullChild) {
Log.w(LOG_TAG, String.format("parent = %s", fromNode.toString()));
}
hasNullChild = true;
continue;
}
if (!childNode.isVisibleToUser()) {
if (DEBUG)
Log.d(LOG_TAG,
String.format("Skipping invisible child: %s", childNode.toString()));
continue;
}
AccessibilityNodeInfo retNode = findNodePatternRecursive(
subSelector, childNode, i, originalPattern);
if (retNode != null) {
return retNode;
}
}
return null;
}
private void enforceNodeTreeConsistent(List<AccessibilityNodeInfo> nodes) {
LongSparseArray<AccessibilityNodeInfo> nodeMap =
new LongSparseArray<AccessibilityNodeInfo>();
final int nodeCount = nodes.size();
for (int i = 0; i < nodeCount; i++) {
AccessibilityNodeInfo node = nodes.get(i);
nodeMap.put(node.getSourceNodeId(), node);
}
// If the nodes are a tree it does not matter from
// which node we start to search for the root.
AccessibilityNodeInfo root = nodeMap.valueAt(0);
AccessibilityNodeInfo parent = root;
while (parent != null) {
root = parent;
parent = nodeMap.get(parent.getParentNodeId());
}
// Traverse the tree and do some checks.
AccessibilityNodeInfo accessFocus = null;
AccessibilityNodeInfo inputFocus = null;
HashSet<AccessibilityNodeInfo> seen = new HashSet<AccessibilityNodeInfo>();
Queue<AccessibilityNodeInfo> fringe = new LinkedList<AccessibilityNodeInfo>();
fringe.add(root);
while (!fringe.isEmpty()) {
AccessibilityNodeInfo current = fringe.poll();
// Check for duplicates
if (!seen.add(current)) {
throw new IllegalStateException("Duplicate node: "
+ current + " in window:"
+ mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
}
// Check for one accessibility focus.
if (current.isAccessibilityFocused()) {
if (accessFocus != null) {
throw new IllegalStateException("Duplicate accessibility focus:"
+ current
+ " in window:" + mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
} else {
accessFocus = current;
}
}
// Check for one input focus.
if (current.isFocused()) {
if (inputFocus != null) {
throw new IllegalStateException("Duplicate input focus: "
+ current + " in window:"
+ mViewRootImpl.mAttachInfo.mAccessibilityWindowId);
} else {
inputFocus = current;
}
}
final int childCount = current.getChildCount();
for (int j = 0; j < childCount; j++) {
final long childId = current.getChildId(j);
final AccessibilityNodeInfo child = nodeMap.get(childId);
if (child != null) {
fringe.add(child);
}
}
}
// Check for disconnected nodes.
for (int j = nodeMap.size() - 1; j >= 0; j--) {
AccessibilityNodeInfo info = nodeMap.valueAt(j);
if (!seen.contains(info)) {
throw new IllegalStateException("Disconnected node: " + info);
}
}
}
/**
* 好友:
* 姓名 1 android.widget.TextView
* 消息 5-[]-last android.widget.TextView focusable
* 文本框 7-0 android.widget.EditText
* 发送按钮 7-1 android.widget.Button
* <p>
* 群:
* 群名 1
* 消息 4/5-
* <p>
* 群里的临时会话:
* 姓名 1-0
* 消息 6-[]-last
* 文本框 8-0
* 发送按钮 8-1
*/
protected boolean init(AccessibilityNodeInfo root) {
//16 是其他界面
//14 是没有聊过天
//12 是发送完消息
if (root.getChildCount() < 12) {
Log.v(TAG, "init: root.childCount: " + root.getChildCount());
return false;
}
// List<AccessibilityNodeInfo> inputList;
// List<AccessibilityNodeInfo> sendList;
// inputList = root.findAccessibilityNodeInfosByViewId(IdInput);
// sendList = root.findAccessibilityNodeInfosByViewId(IdSend);
// if (inputList.size() == 0) {
// Log.d(TAG, "init: input is null, return");
// return false;
// }
// if (sendList.size() == 0) {
// Log.d(TAG, "init: send button is null, return");
// return false;
// }
// inputNode = inputList.get(0);
// sendBtnNode = sendList.get(0);
List<AccessibilityNodeInfo> titleList;
titleList = root.findAccessibilityNodeInfosByViewId(IdTitle);
if (titleList.size() == 0) {
Log.d(TAG, "init: title is null, return");
return false;
}
titleNode = titleList.get(0);
if (titleNode.getText() == null) {
Log.d(TAG, "init: name is null,return");
return false;
}
title = titleNode.getText() + "";
isOtherMsg = false;
for (int i = 4; i < 7; i++) {
AccessibilityNodeInfo child = root.getChild(i);
String name = child.getViewIdResourceName();
if (name == null)
continue;
switch (name) {
case IdChatGroupView:
chatGroupViewNode = child;
break;
case IdOtherMsg:
otherMsgNode = child;
isOtherMsg = true;
break;
}
}
if (chatGroupViewNode == null) {
Log.i(TAG, "init: chatGroupViewNode is null, return");
return false;
}
return true;
}
/**
* 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(AccessibilityNodeInfo node) {
if (node == null) {
return null;
}
CharSequence text = node.getText();
if (!TextUtils.isEmpty(text)
&& (TextUtils.getTrimmedLength(text) > 0)) {
return text;
}
//Use contentDescription only if there are no child nodes -
if (node.getChildCount() == 0) {
final CharSequence contentDescription = node.getContentDescription();
if (!TextUtils.isEmpty(contentDescription)
&& (TextUtils.getTrimmedLength(contentDescription) > 0)) {
return contentDescription;
}
}
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;
// }
//
// CharSequence text = node.getText();
// if (!TextUtils.isEmpty(text)
// && (TextUtils.getTrimmedLength(text) > 0)) {
// return text;
// }
//
// return null;
}