1package com.android.testing.uiautomation;
2
3import org.xmlpull.v1.XmlSerializer;
4
5import android.graphics.Rect;
6import android.os.Environment;
7import android.os.SystemClock;
8import android.util.Log;
9import android.util.Xml;
10import android.view.accessibility.AccessibilityNodeInfo;
11
12import java.io.File;
13import java.io.FileWriter;
14import java.io.IOException;
15import java.io.StringWriter;
16import java.util.LinkedList;
17import java.util.Queue;
18
19public class AccessibilityNodeInfoHelper {
20
21    private static final String LOGTAG = "AccessibilityNodeInfoHelper";
22
23    public static void dumpWindowToFile(AccessibilityNodeInfo info) {
24        AccessibilityNodeInfo root = getRootAccessibilityNodeInfo(info);
25        if (root == null) {
26            return;
27        }
28        final long startTime = SystemClock.uptimeMillis();
29        try {
30            File baseDir = new File(Environment.getDataDirectory(), "uidump");
31            if (!baseDir.exists()) {
32                baseDir.mkdir();
33                baseDir.setExecutable(true, false);
34                baseDir.setWritable(true, false);
35                baseDir.setReadable(true, false);
36            }
37            FileWriter writer = new FileWriter(
38                    new File(baseDir, "window_dump.xml"));
39            XmlSerializer serializer = Xml.newSerializer();
40            StringWriter stringWriter = new StringWriter();
41            serializer.setOutput(stringWriter);
42            serializer.startDocument("UTF-8", true);
43            serializer.startTag("", "hierarchy");
44            dumpNodeRec(root, serializer, 0);
45            if (root != info)
46                root.recycle();
47            serializer.endTag("", "hierarchy");
48            serializer.endDocument();
49            writer.write(stringWriter.toString());
50            writer.close();
51        } catch (IOException e) {
52            Log.e(LOGTAG, "failed to dump window to file", e);
53        }
54        final long endTime = SystemClock.uptimeMillis();
55        Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms");
56    }
57
58    public static  void dumpNodeRec(AccessibilityNodeInfo node, XmlSerializer serializer, int index)
59        throws IOException {
60        serializer.startTag("", "node");
61        serializer.attribute("", "index", Integer.toString(index));
62        serializer.attribute("", "text", safeCharSeqToString(node.getText()));
63        serializer.attribute("", "class", safeCharSeqToString(node.getClassName()));
64        serializer.attribute("", "package", safeCharSeqToString(node.getPackageName()));
65        serializer.attribute("", "content-desc", safeCharSeqToString(node.getContentDescription()));
66        serializer.attribute("", "checkable", Boolean.toString(node.isCheckable()));
67        serializer.attribute("", "checked", Boolean.toString(node.isChecked()));
68        serializer.attribute("", "clickable", Boolean.toString(node.isClickable()));
69        serializer.attribute("", "enabled", Boolean.toString(node.isEnabled()));
70        serializer.attribute("", "focusable", Boolean.toString(node.isFocusable()));
71        serializer.attribute("", "focused", Boolean.toString(node.isFocused()));
72        serializer.attribute("", "long-clickable", Boolean.toString(node.isLongClickable()));
73        serializer.attribute("", "password", Boolean.toString(node.isPassword()));
74        serializer.attribute("", "selected", Boolean.toString(node.isSelected()));
75        Rect bounds = new Rect();
76        node.getBoundsInScreen(bounds);
77        serializer.attribute("", "bounds", bounds.toShortString());
78        for (int i = 0; i < node.getChildCount(); i++) {
79            AccessibilityNodeInfo child = node.getChild(i);
80            if (child != null) {
81                dumpNodeRec(child, serializer, i);
82                child.recycle();
83            }
84        }
85        serializer.endTag("", "node");
86    }
87
88    public static  void dumpWindow(AccessibilityNodeInfo info) {
89        AccessibilityNodeInfo root = getRootAccessibilityNodeInfo(info);
90        if (root == null) {
91            return;
92        }
93        final long startTime = SystemClock.uptimeMillis();
94        Queue<AccessibilityNodeInfo> mFringe = new LinkedList<AccessibilityNodeInfo>();
95        mFringe.add(root);
96        int fetchedNodeCount = 0;
97        while (!mFringe.isEmpty()) {
98            AccessibilityNodeInfo current = mFringe.poll();
99            Log.d(LOGTAG, String.format("class: %s; text: %s; content-desc: %s",
100                    current.getClassName(),
101                    current.getText(),
102                    current.getContentDescription()));
103            fetchedNodeCount++;
104            final int childCount = current.getChildCount();
105            for (int i = 0; i < childCount; i++) {
106                AccessibilityNodeInfo child = current.getChild(i);
107                if (child != null) {
108                    mFringe.add(child);
109                }
110            }
111        }
112        final long endTime = SystemClock.uptimeMillis();
113        Log.w(LOGTAG, "Fetch time: " + (endTime - startTime) + "ms; fetchedNodeCount: "
114                + fetchedNodeCount);
115    }
116
117    public static  void dumpNode(AccessibilityNodeInfo node) {
118      Log.d(LOGTAG, String.format("class: %s; text: %s; content-desc: %s",
119              node.getClassName(),
120              node.getText(),
121              node.getContentDescription()));
122    }
123
124    public static  void dumpChildren(AccessibilityNodeInfo node) {
125        int count = node.getChildCount();
126        for (int i = 0; i < count; i++) {
127            AccessibilityNodeInfo child = node.getChild(i);
128            dumpNode(child);
129            child.recycle();
130        }
131    }
132
133    public static  AccessibilityNodeInfo getRootAccessibilityNodeInfo(AccessibilityNodeInfo info) {
134        if (info == null)
135            return null;
136        AccessibilityNodeInfo root = info.getParent();
137        while (root != null) {
138            AccessibilityNodeInfo parent = root.getParent();
139            if (parent != null) {
140                root.recycle();
141                root = parent;
142            } else {
143                break;
144            }
145        }
146        return root == null ? info : root;
147    }
148
149    public static String safeCharSeqToString(CharSequence cs) {
150        if (cs == null)
151            return "[null]";
152        else
153            return cs.toString();
154    }
155}
156