ViewDebug.java revision c39a6e0c51e182338deb8b63d07933b585134929
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.view;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
20c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.util.DisplayMetrics;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Bitmap;
23c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.graphics.Canvas;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Environment;
25c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.os.Debug;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.BufferedWriter;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileWriter;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.DataOutputStream;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStreamWriter;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.BufferedOutputStream;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStream;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.LinkedList;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashMap;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.CountDownLatch;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.TimeUnit;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.Target;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.ElementType;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.Retention;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.RetentionPolicy;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.Field;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.Method;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.InvocationTargetException;
49c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport java.lang.reflect.AccessibleObject;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Various debugging/tracing tools related to {@link View} and the view hierarchy.
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class ViewDebug {
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables or disables view hierarchy tracing. Any invoker of
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * check that this value is set to true as not to affect performance.
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final boolean TRACE_HIERARCHY = false;
619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables or disables view recycler tracing. Any invoker of
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])} should first
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * check that this value is set to true as not to affect performance.
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final boolean TRACE_RECYCLER = false;
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The system property of dynamic switch for capturing view information
719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * when it is set, we dump interested fields and methods for the view on focus
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String SYSTEM_PROPERTY_CAPTURE_VIEW = "debug.captureview";
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The system property of dynamic switch for capturing event information
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * when it is set, we log key events, touch/motion and trackball events
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent";
80c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This annotation can be used to mark fields and methods to be dumped by
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the view server. Only non-void methods with no arguments can be annotated
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * by this annotation.
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.FIELD, ElementType.METHOD })
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface ExportedProperty {
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When resolveId is true, and if the annotated field/method return value
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * is an int, the value is converted to an Android's resource name.
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the property's value must be transformed into an Android
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *         resource name, false otherwise
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean resolveId() default false;
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * A mapping can be defined to map int values to specific strings. For
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * instance, View.getVisibility() returns 0, 4 or 8. However, these values
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * actually mean VISIBLE, INVISIBLE and GONE. A mapping can be used to see
1029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * these human readable values:
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <pre>
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @ViewDebug.ExportedProperty(mapping = {
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 0, to = "VISIBLE"),
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 8, to = "GONE")
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * })
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * public int getVisibility() { ...
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <pre>
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An array of int to String mappings
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see android.view.ViewDebug.IntToString
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        IntToString[] mapping() default { };
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
120c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * A mapping can be defined to map array indices to specific strings.
121c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * A mapping can be used to see human readable values for the indices
122c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * of an array:
123c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
124c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * <pre>
125c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @ViewDebug.ExportedProperty(mapping = {
126c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 0, to = "INVALID"),
127c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 1, to = "FIRST"),
128c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 2, to = "SECOND")
129c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * })
130c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * private int[] mElements;
131c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * <pre>
132c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
133c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @return An array of int to String mappings
134c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
135c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @see android.view.ViewDebug.IntToString
136c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @see #mapping()
137c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         */
138c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        IntToString[] indexMapping() default { };
139c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
140c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        /**
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When deep export is turned on, this property is not dumped. Instead, the
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * properties contained in this property are dumped. Each child property
1439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * is prefixed with the name of this property.
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the properties of this property should be dumped
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see #prefix()
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean deepExport() default false;
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The prefix to use on child properties when deep export is enabled
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return a prefix as a String
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see #deepExport()
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String prefix() default "";
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines a mapping from an int value to a String. Such a mapping can be used
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in a @ExportedProperty to provide more meaningful values to the end user.
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see android.view.ViewDebug.ExportedProperty
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.TYPE })
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface IntToString {
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The original int value to map to a String.
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An arbitrary int value.
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int from();
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The String to use in place of the original int value.
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An arbitrary non-null String.
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String to();
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This annotation can be used to mark fields and methods to be dumped when
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the view is captured. Methods with this annotation must have no arguments
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and must return <some type of data>.
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide pending API Council approval
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.FIELD, ElementType.METHOD })
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface CapturedViewProperty {
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When retrieveReturn is true, we need to retrieve second level methods
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod()
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * we will set retrieveReturn = true on the annotation of
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * myView.getFirstLevelMethod()
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if we need the second level methods
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean retrieveReturn() default false;
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
2069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
2079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Maximum delay in ms after which we stop trying to capture a View's drawing
2099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CAPTURE_TIMEOUT = 4000;
2109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
2129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_DUMP = "DUMP";
2139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
2149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
215c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static final String REMOTE_PROFILE = "PROFILE";
2169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Field[]> sFieldsForClasses;
2189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Method[]> sMethodsForClasses;
219c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
220c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
2219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines the type of hierarhcy trace to output to the hierarchy traces file.
2239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum HierarchyTraceType {
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE,
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE_CHILD,
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE_CHILD_IN_PARENT,
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        REQUEST_LAYOUT,
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ON_LAYOUT,
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ON_MEASURE,
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        DRAW,
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BUILD_CACHE
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static BufferedWriter sHierarchyTraces;
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static ViewRoot sHierarhcyRoot;
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sHierarchyTracePrefix;
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines the type of recycler trace to output to the recycler traces file.
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum RecyclerTraceType {
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        NEW_VIEW,
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BIND_VIEW,
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RECYCLE_FROM_ACTIVE_HEAP,
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RECYCLE_FROM_SCRAP_HEAP,
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_TO_ACTIVE_HEAP,
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_TO_SCRAP_HEAP,
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class RecyclerTrace {
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int view;
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public RecyclerTraceType type;
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int position;
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int indexOnScreen;
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static View sRecyclerOwnerView;
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static List<View> sRecyclerViews;
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static List<RecyclerTrace> sRecyclerTraces;
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sRecyclerTracePrefix;
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of instanciated Views.
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The number of Views instanciated in the current process.
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static long getViewInstanceCount() {
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return View.sInstanceCount;
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of instanciated ViewRoots.
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The number of ViewRoots instanciated in the current process.
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static long getViewRootInstanceCount() {
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ViewRoot.getInstanceCount();
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Outputs a trace to the currently opened recycler traces. The trace records the type of
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * recycler action performed on the supplied view as well as a number of parameters.
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view to trace
2919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param type the type of the trace
2929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param parameters parameters depending on the type of the trace
2939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void trace(View view, RecyclerTraceType type, int... parameters) {
2959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
2969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
2979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
2989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!sRecyclerViews.contains(view)) {
3009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sRecyclerViews.add(view);
3019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int index = sRecyclerViews.indexOf(view);
3049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RecyclerTrace trace = new RecyclerTrace();
3069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.view = index;
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.type = type;
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.position = parameters[0];
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.indexOnScreen = parameters[1];
3109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces.add(trace);
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Starts tracing the view recycler of the specified view. The trace is identified by a prefix,
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used to build the traces files names: <code>/EXTERNAL/view-recycler/PREFIX.traces</code> and
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>.
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one view recycler can be traced at the same time. After calling this method, any
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * other invocation will result in a <code>IllegalStateException</code> unless
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #stopRecyclerTracing()} is invoked before.
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Traces files are created only after {@link #stopRecyclerTracing()} is invoked.
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_RECYCLER is false.
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param prefix the traces files name prefix
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view whose recycler must be traced
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #stopRecyclerTracing()
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void startRecyclerTracing(String prefix, View view) {
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_RECYCLER) {
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView != null) {
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call stopRecyclerTracing() before running" +
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " a new trace!");
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTracePrefix = prefix;
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerOwnerView = view;
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews = new ArrayList<View>();
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces = new LinkedList<RecyclerTrace>();
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stops the current view recycer tracing.
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.traces</code>
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all the traces (or method calls) relative to the specified view's recycler.
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all of the views used by the recycler of the view supplied to
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #startRecyclerTracing(String, View)}.
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_RECYCLER is false.
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #startRecyclerTracing(String, View)
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void stopRecyclerTracing() {
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_RECYCLER) {
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call startRecyclerTracing() before" +
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " stopRecyclerTracing()!");
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
377c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump.mkdirs();
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler");
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final BufferedWriter out = new BufferedWriter(new FileWriter(recyclerDump), 8 * 1024);
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (View view : sRecyclerViews) {
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final String name = view.getClass().getName();
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(name);
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.newLine();
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.close();
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump recycler content");
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final FileOutputStream file = new FileOutputStream(recyclerDump);
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final DataOutputStream out = new DataOutputStream(file);
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (RecyclerTrace trace : sRecyclerTraces) {
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.view);
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.type.ordinal());
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.position);
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.indexOnScreen);
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.flush();
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.close();
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump recycler traces");
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews.clear();
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews = null;
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces.clear();
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces = null;
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerOwnerView = null;
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Outputs a trace to the currently opened traces file. The trace contains the class name
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and instance's hashcode of the specified view as well as the supplied trace type.
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view to trace
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param type the type of the trace
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void trace(View view, HierarchyTraceType type) {
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarchyTraces == null) {
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(type.name());
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(' ');
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(view.getClass().getName());
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write('@');
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(Integer.toHexString(view.hashCode()));
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.newLine();
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping trace of type " + type + " for view " + view);
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Starts tracing the view hierarchy of the specified view. The trace is identified by a prefix,
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used to build the traces files names: <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code> and
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>.
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one view hierarchy can be traced at the same time. After calling this method, any
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * other invocation will result in a <code>IllegalStateException</code> unless
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #stopHierarchyTracing()} is invoked before.
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all the traces (or method calls) relative to the specified view's hierarchy.
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_HIERARCHY is false.
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param prefix the traces files name prefix
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view whose hierarchy must be traced
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #stopHierarchyTracing()
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void startHierarchyTracing(String prefix, View view) {
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_HIERARCHY) {
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarhcyRoot != null) {
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " a new trace!");
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
481c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump.mkdirs();
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump = new File(hierarchyDump, prefix + ".traces");
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarchyTracePrefix = prefix;
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump view hierarchy");
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarhcyRoot = (ViewRoot) view.getRootView().getParent();
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stops the current view hierarchy tracing. This method closes the file
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>.
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing the view hierarchy of the view supplied to
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #startHierarchyTracing(String, View)}.
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_HIERARCHY is false.
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #startHierarchyTracing(String, View)
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void stopHierarchyTracing() {
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_HIERARCHY) {
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarhcyRoot == null || sHierarchyTraces == null) {
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call startHierarchyTracing() before" +
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " stopHierarchyTracing()!");
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.close();
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not write view traces");
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarchyTraces = null;
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
529c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump.mkdirs();
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree");
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedWriter out;
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump view hierarchy");
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        View view = sHierarhcyRoot.getView();
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view instanceof ViewGroup) {
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ViewGroup group = (ViewGroup) view;
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dumpViewHierarchy(group, out, 0);
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e("View", "Could not dump view hierarchy");
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarhcyRoot = null;
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void dispatchCommand(View view, String command, String parameters,
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            OutputStream clientStream) throws IOException {
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Paranoid but safe...
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        view = view.getRootView();
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dump(view, clientStream);
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final String[] params = parameters.split(" ");
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                capture(view, clientStream, params[0]);
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (REMOTE_COMMAND_INVALIDATE.equalsIgnoreCase(command)) {
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                invalidate(view, params[0]);
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) {
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                requestLayout(view, params[0]);
571c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } else if (REMOTE_PROFILE.equalsIgnoreCase(command)) {
572c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                profile(view, clientStream, params[0]);
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
577c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static View findView(View root, String parameter) {
578c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        // Look by type/hashcode
579c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (parameter.indexOf('@') != -1) {
580c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final String[] ids = parameter.split("@");
581c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final String className = ids[0];
582c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int hashCode = Integer.parseInt(ids[1], 16);
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
584c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            View view = root.getRootView();
585c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (view instanceof ViewGroup) {
586c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                return findView((ViewGroup) view, className, hashCode);
587c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
588c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
589c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            // Look by id
590c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int id = root.getResources().getIdentifier(parameter, null, null);
591c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            return root.getRootView().findViewById(id);
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void invalidate(View root, String parameter) {
598c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            view.postInvalidate();
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void requestLayout(View root, String parameter) {
605c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            root.post(new Runnable() {
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                public void run() {
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    view.requestLayout();
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            });
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
615c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void profile(View root, OutputStream clientStream, String parameter)
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws IOException {
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
618c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
619c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        BufferedWriter out = null;
620c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        try {
621c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
622c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
623c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (view != null) {
624c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() {
625c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public Void[] pre() {
626c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        forceLayout(view);
627c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        return null;
628c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
629c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
630c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    private void forceLayout(View view) {
631c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        view.forceLayout();
632c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        if (view instanceof ViewGroup) {
633c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                            ViewGroup group = (ViewGroup) view;
634c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                            final int count = group.getChildCount();
635c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                            for (int i = 0; i < count; i++) {
636c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                                forceLayout(group.getChildAt(i));
637c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                            }
638c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        }
639c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
640c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
641c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void run(Void... data) {
642c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
643c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
644c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
645c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void post(Void... data) {
646c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
647c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                });
648c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
649c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
650c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public Void[] pre() {
651c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        return null;
652c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
653c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
654c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void run(Void... data) {
655c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
656c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
657c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
658c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void post(Void... data) {
659c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
660c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                });
661c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
662c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() {
663c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public Object[] pre() {
664c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        final DisplayMetrics metrics = view.getResources().getDisplayMetrics();
665c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        final Bitmap bitmap = Bitmap.createBitmap(metrics.widthPixels,
666c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                                metrics.heightPixels, Bitmap.Config.ARGB_8888);
667c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        final Canvas canvas = new Canvas(bitmap);
668c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        return new Object[] { bitmap, canvas };
669c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
670c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
671c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void run(Object... data) {
672c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        view.draw((Canvas) data[1]);
673c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
674c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
675c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void post(Object... data) {
676c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        ((Bitmap) data[0]).recycle();
677c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
678c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                });
679c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
680c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(String.valueOf(durationMeasure));
681c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(' ');
682c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(String.valueOf(durationLayout));
683c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(' ');
684c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(String.valueOf(durationDraw));
685c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.newLine();
686c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } else {
687c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write("-1 -1 -1");
688c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.newLine();
689c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
690c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (Exception e) {
691c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            android.util.Log.w("View", "Problem profiling the view:", e);
692c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } finally {
693c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (out != null) {
694c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.close();
695c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
696c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
697c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
698c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
699c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    interface ViewOperation<T> {
700c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        T[] pre();
701c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        void run(T... data);
702c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        void post(T... data);
703c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
704c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
705c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) {
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final CountDownLatch latch = new CountDownLatch(1);
707c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final long[] duration = new long[1];
708c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
709c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        view.post(new Runnable() {
710c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            public void run() {
711c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                try {
712c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    T[] data = operation.pre();
713c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    long start = Debug.threadCpuTimeNanos();
714c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    operation.run(data);
715c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    duration[0] = Debug.threadCpuTimeNanos() - start;
716c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    operation.post(data);
717c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } finally {
718c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    latch.countDown();
719c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
720c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
721c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        });
722c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
723c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        try {
724c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
725c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (InterruptedException e) {
726c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            Log.w("View", "Could not complete the profiling of the view " + view);
727c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            Thread.currentThread().interrupt();
728c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            return -1;
729c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
730c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
731c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        return duration[0];
732c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
733c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
734c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void capture(View root, final OutputStream clientStream, String parameter)
735c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            throws IOException {
736c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
737c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View captureView = findView(root, parameter);
7389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (captureView != null) {
740c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final CountDownLatch latch = new CountDownLatch(1);
7419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Bitmap[] cache = new Bitmap[1];
7429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final boolean hasCache = captureView.isDrawingCacheEnabled();
7449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final boolean willNotCache = captureView.willNotCacheDrawing();
7459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (willNotCache) {
747c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                // TODO: Should happen on the UI thread
7489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                captureView.setWillNotCacheDrawing(false);
7499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            root.post(new Runnable() {
7529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                public void run() {
7539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
7549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (!hasCache) {
7559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            captureView.buildDrawingCache();
7569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
7579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        cache[0] = captureView.getDrawingCache();
7599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } finally {
7609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        latch.countDown();
7619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
7629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            });
7649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
7669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
7679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (cache[0] != null) {
7699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    BufferedOutputStream out = null;
7709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
7719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out = new BufferedOutputStream(clientStream, 32 * 1024);
7729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        cache[0].compress(Bitmap.CompressFormat.PNG, 100, out);
7739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out.flush();
7749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } finally {
7759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (out != null) {
7769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            out.close();
7779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
7789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
7799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (InterruptedException e) {
781c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                Log.w("View", "Could not complete the capture of the view " + captureView);
782c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                Thread.currentThread().interrupt();
7839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } finally {
7849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (willNotCache) {
785c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    // TODO: Should happen on the UI thread
7869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    captureView.setWillNotCacheDrawing(true);
7879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (!hasCache) {
789c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    // TODO: Should happen on the UI thread
7909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    captureView.destroyDrawingCache();
7919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dump(View root, OutputStream clientStream) throws IOException {
7979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedWriter out = null;
7989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
7999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
8009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            View view = root.getRootView();
8019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
8029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ViewGroup group = (ViewGroup) view;
8039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpViewHierarchyWithProperties(group, out, 0);
8049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write("DONE.");
8069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
807c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (Exception e) {
808c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            android.util.Log.w("View", "Problem dumping the view:", e);
8099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
8109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (out != null) {
8119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
8129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static View findView(ViewGroup group, String className, int hashCode) {
8179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isRequestedView(group, className, hashCode)) {
8189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return group;
8199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
8229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
8239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
8249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
8259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final View found = findView((ViewGroup) view, className, hashCode);
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (found != null) {
8279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return found;
8289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (isRequestedView(view, className, hashCode)) {
8309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return view;
8319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
8359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean isRequestedView(View view, String className, int hashCode) {
8389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
8399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dumpViewHierarchyWithProperties(ViewGroup group,
8429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            BufferedWriter out, int level) {
8439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!dumpViewWithProperties(group, out, level)) {
8449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
8459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
8489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
8499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
8509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
8519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpViewHierarchyWithProperties((ViewGroup) view, out, level + 1);
8529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
8539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpViewWithProperties(view, out, level + 1);
8549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean dumpViewWithProperties(View view, BufferedWriter out, int level) {
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < level; i++) {
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(' ');
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(view.getClass().getName());
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write('@');
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(Integer.toHexString(view.hashCode()));
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(' ');
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dumpViewProperties(view, out);
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping hierarchy tree");
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Field[] getExportedPropertyFields(Class<?> klass) {
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sFieldsForClasses == null) {
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sFieldsForClasses = new HashMap<Class<?>, Field[]>();
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
880c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (sAnnotations == null) {
881c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
882c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
883c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
885c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Field[] fields = map.get(klass);
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fields != null) {
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fields;
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Field> foundFields = new ArrayList<Field>();
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = klass.getDeclaredFields();
8949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (field.isAnnotationPresent(ExportedProperty.class)) {
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                field.setAccessible(true);
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundFields.add(field);
901c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                annotations.put(field, field.getAnnotation(ExportedProperty.class));
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = foundFields.toArray(new Field[foundFields.size()]);
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, fields);
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fields;
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Method[] getExportedPropertyMethods(Class<?> klass) {
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sMethodsForClasses == null) {
913c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
914c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
915c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (sAnnotations == null) {
916c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
918c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
920c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Method[] methods = map.get(klass);
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (methods != null) {
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return methods;
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Method> foundMethods = new ArrayList<Method>();
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = klass.getDeclaredMethods();
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (method.getParameterTypes().length == 0 &&
934c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    method.isAnnotationPresent(ExportedProperty.class) &&
935c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    method.getReturnType() != Void.class) {
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                method.setAccessible(true);
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundMethods.add(method);
938c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                annotations.put(method, method.getAnnotation(ExportedProperty.class));
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = foundMethods.toArray(new Method[foundMethods.size()]);
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, methods);
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return methods;
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dumpViewProperties(Object view, BufferedWriter out) throws IOException {
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        dumpViewProperties(view, out, "");
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dumpViewProperties(Object view, BufferedWriter out, String prefix)
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws IOException {
9549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> klass = view.getClass();
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        do {
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            exportFields(view, out, klass, prefix);
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            exportMethods(view, out, klass, prefix);
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            klass = klass.getSuperclass();
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } while (klass != Object.class);
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void exportMethods(Object view, BufferedWriter out, Class<?> klass,
9649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String prefix) throws IOException {
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Method[] methods = getExportedPropertyMethods(klass);
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
9699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
9709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
9719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection EmptyCatchBlock
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // TODO: This should happen on the UI thread
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object methodValue = method.invoke(view, (Object[]) null);
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> returnType = method.getReturnType();
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (returnType == int.class) {
978c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(method);
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.resolveId() && view instanceof View) {
9809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final int id = (Integer) methodValue;
981c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        methodValue = resolveId(view, id);
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final IntToString[] mapping = property.mapping();
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mapping.length > 0) {
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            final int intValue = (Integer) methodValue;
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            boolean mapped = false;
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            int mappingCount = mapping.length;
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            for (int j = 0; j < mappingCount; j++) {
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                final IntToString mapper = mapping[j];
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                if (mapper.from() == intValue) {
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    methodValue = mapper.to();
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    mapped = true;
9939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    break;
9949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                }
9959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
9969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (!mapped) {
9989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                methodValue = intValue;
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1002c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } else if (returnType == int[].class) {
1003c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(method);
1004c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final int[] array = (int[]) methodValue;
1005c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String valuePrefix = prefix + method.getName() + '_';
1006c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String suffix = "()";
1007c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1008c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    exportUnrolledArray(view, out, property, array, valuePrefix, suffix);
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!returnType.isPrimitive()) {
1010c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(method);
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.deepExport()) {
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        dumpViewProperties(methodValue, out, prefix + property.prefix());
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
10149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1017c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                writeEntry(out, prefix, method.getName(), "()", methodValue);
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (InvocationTargetException e) {
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void exportFields(Object view, BufferedWriter out, Class<?> klass, String prefix)
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws IOException {
10269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Field[] fields = getExportedPropertyFields(klass);
10279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
10299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
10319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection EmptyCatchBlock
10339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object fieldValue = null;
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> type = field.getType();
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (type == int.class) {
1038c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(field);
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.resolveId() && view instanceof View) {
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final int id = field.getInt(view);
1041c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        fieldValue = resolveId(view, id);
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final IntToString[] mapping = property.mapping();
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mapping.length > 0) {
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            final int intValue = field.getInt(view);
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            int mappingCount = mapping.length;
10479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            for (int j = 0; j < mappingCount; j++) {
10489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                final IntToString mapped = mapping[j];
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                if (mapped.from() == intValue) {
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    fieldValue = mapped.to();
10519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    break;
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                }
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (fieldValue == null) {
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                fieldValue = intValue;
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1060c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } else if (type == int[].class) {
1061c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(field);
1062c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final int[] array = (int[]) field.get(view);
1063c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String valuePrefix = prefix + field.getName() + '_';
1064c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String suffix = "";
1065c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1066c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    exportUnrolledArray(view, out, property, array, valuePrefix, suffix);
1067c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1068c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    // We exit here!
1069c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    return;
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!type.isPrimitive()) {
1071c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(field);
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.deepExport()) {
10739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        dumpViewProperties(field.get(view), out, prefix + property.prefix());
10749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fieldValue == null) {
10799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fieldValue = field.get(view);
10809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1082c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                writeEntry(out, prefix, field.getName(), "", fieldValue);
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1088c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void writeEntry(BufferedWriter out, String prefix, String name,
1089c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String suffix, Object value) throws IOException {
1090c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1091c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(prefix);
1092c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(name);
1093c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(suffix);
1094c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write("=");
1095c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        writeValue(out, value);
1096c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(' ');
1097c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1098c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1099c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void exportUnrolledArray(Object view, BufferedWriter out,
1100c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            ExportedProperty property, int[] array, String prefix, String suffix)
1101c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            throws IOException {
1102c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1103c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final IntToString[] indexMapping = property.indexMapping();
1104c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean hasIndexMapping = indexMapping.length > 0;
1105c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1106c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final IntToString[] mapping = property.mapping();
1107c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean hasMapping = mapping.length > 0;
1108c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1109c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean resolveId = property.resolveId() && view instanceof View;
1110c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final int valuesCount = array.length;
1111c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1112c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        for (int j = 0; j < valuesCount; j++) {
1113c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String name;
1114c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String value;
1115c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1116c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int intValue = array[j];
1117c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1118c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            name = String.valueOf(j);
1119c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (hasIndexMapping) {
1120c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                int mappingCount = indexMapping.length;
1121c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                for (int k = 0; k < mappingCount; k++) {
1122c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final IntToString mapped = indexMapping[k];
1123c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    if (mapped.from() == j) {
1124c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        name = mapped.to();
1125c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        break;
1126c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
1127c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1128c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1129c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1130c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            value = String.valueOf(intValue);
1131c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (hasMapping) {
1132c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                int mappingCount = mapping.length;
1133c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                for (int k = 0; k < mappingCount; k++) {
1134c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final IntToString mapped = mapping[k];
1135c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    if (mapped.from() == intValue) {
1136c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        value = mapped.to();
1137c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        break;
1138c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
1139c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1140c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1141c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1142c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (resolveId) {
1143c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                value = (String) resolveId(view, intValue);
1144c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1145c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1146c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            writeEntry(out, prefix, name, suffix, value);
1147c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1148c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1149c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1150c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static Object resolveId(Object view, int id) {
1151c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        Object fieldValue;
1152c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final Resources resources = ((View) view).getContext().getResources();
1153c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (id >= 0) {
1154c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            try {
1155c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                fieldValue = resources.getResourceTypeName(id) + '/' +
1156c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        resources.getResourceEntryName(id);
1157c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } catch (Resources.NotFoundException e) {
1158c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                fieldValue = "id/0x" + Integer.toHexString(id);
1159c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1160c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
1161c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            fieldValue = "NO_ID";
1162c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1163c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        return fieldValue;
1164c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1165c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1166c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void writeValue(BufferedWriter out, Object value) throws IOException {
1167c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (value != null) {
1168c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String output = value.toString().replace("\n", "\\n");
1169c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(String.valueOf(output.length()));
1170c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(",");
1171c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(output);
1172c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
1173c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write("4,null");
1174c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1175c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1176c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dumpViewHierarchy(ViewGroup group, BufferedWriter out, int level) {
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!dumpView(group, out, level)) {
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpViewHierarchy((ViewGroup) view, out, level + 1);
11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpView(view, out, level + 1);
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean dumpView(Object view, BufferedWriter out, int level) {
11949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < level; i++) {
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(' ');
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(view.getClass().getName());
11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write('@');
12009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(Integer.toHexString(view.hashCode()));
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping hierarchy tree");
12049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mCapturedViewFieldsForClasses == null) {
12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Field[] fields = map.get(klass);
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fields != null) {
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fields;
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Field> foundFields = new ArrayList<Field>();
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = klass.getFields();
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (field.isAnnotationPresent(CapturedViewProperty.class)) {
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                field.setAccessible(true);
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundFields.add(field);
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = foundFields.toArray(new Field[foundFields.size()]);
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, fields);
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fields;
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mCapturedViewMethodsForClasses == null) {
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Method[] methods = map.get(klass);
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (methods != null) {
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return methods;
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Method> foundMethods = new ArrayList<Method>();
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = klass.getMethods();
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
12549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (method.getParameterTypes().length == 0 &&
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    method.isAnnotationPresent(CapturedViewProperty.class) &&
12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    method.getReturnType() != Void.class) {
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                method.setAccessible(true);
12599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundMethods.add(method);
12609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = foundMethods.toArray(new Method[foundMethods.size()]);
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, methods);
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return methods;
12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String capturedViewExportMethods(Object obj, Class<?> klass,
12709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String prefix) {
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (obj == null) {
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "null";
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Method[] methods = capturedViewGetPropertyMethods(klass);
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
12839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object methodValue = method.invoke(obj, (Object[]) null);
12849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> returnType = method.getReturnType();
12859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (property.retrieveReturn()) {
12889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    //we are interested in the second level data only
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(prefix);
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(method.getName());
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("()=");
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (methodValue != null) {
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final String value = methodValue.toString().replace("\n", "\\n");
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sb.append(value);
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sb.append("null");
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("; ");
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              } catch (IllegalAccessException e) {
13049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //Exception IllegalAccess, it is OK here
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //we simply ignore this method
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              } catch (InvocationTargetException e) {
13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //Exception InvocationTarget, it is OK here
13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //we simply ignore this method
13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              }
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (obj == null) {
13179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "null";
13189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
13219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Field[] fields = capturedViewGetPropertyFields(klass);
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object fieldValue = field.get(obj);
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(prefix);
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(field.getName());
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("=");
13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fieldValue != null) {
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    final String value = fieldValue.toString().replace("\n", "\\n");
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(value);
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("null");
13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(' ');
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                //Exception IllegalAccess, it is OK here
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                //we simply ignore this field
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * dump view info for id based instrument test generation
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (and possibly further data analysis). The results are dumped
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to the log.
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag for log
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view for dump
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide pending API Council approval
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void dumpCapturedView(String tag, Object view) {
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> klass = view.getClass();
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder(klass.getName() + ": ");
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sb.append(capturedViewExportFields(view, klass, ""));
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sb.append(capturedViewExportMethods(view, klass, ""));
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Log.d(tag, sb.toString());
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1365