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
19e551dc76bcde0e114b06e876eeb1d5f9d367fddfRomain Guyimport android.util.Config;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
21c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.util.DisplayMetrics;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
23105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.content.Context;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Bitmap;
25c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.graphics.Canvas;
26223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guyimport android.graphics.Rect;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Environment;
28c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.os.Debug;
29223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guyimport android.os.RemoteException;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
31223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guyimport java.io.ByteArrayOutputStream;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.BufferedWriter;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileWriter;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.DataOutputStream;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStreamWriter;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.BufferedOutputStream;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStream;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.LinkedList;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashMap;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.CountDownLatch;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.TimeUnit;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.Target;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.ElementType;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.Retention;
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.RetentionPolicy;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.Field;
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.Method;
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.InvocationTargetException;
54c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport java.lang.reflect.AccessibleObject;
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Various debugging/tracing tools related to {@link View} and the view hierarchy.
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class ViewDebug {
609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Log tag used to log errors related to the consistency of the view hierarchy.
6213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
6313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
6413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
6513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final String CONSISTENCY_LOG_TAG = "ViewConsistency";
6613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
6713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
6813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Flag indicating the consistency check should check layout-related properties.
6913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
7013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
7113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
7213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final int CONSISTENCY_LAYOUT = 0x1;
7313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
7413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
7513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Flag indicating the consistency check should check drawing-related properties.
7613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
7713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
7813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
7913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final int CONSISTENCY_DRAWING = 0x2;
8013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
8113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables or disables view hierarchy tracing. Any invoker of
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * check that this value is set to true as not to affect performance.
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final boolean TRACE_HIERARCHY = false;
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables or disables view recycler tracing. Any invoker of
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])} should first
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * check that this value is set to true as not to affect performance.
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final boolean TRACE_RECYCLER = false;
94a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
96cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * Enables or disables motion events tracing. Any invoker of
97cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * {@link #trace(View, MotionEvent, MotionEventTraceType)} should first check
98cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * that this value is set to true as not to affect performance.
99cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
100cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @hide
101cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     */
102cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    public static final boolean TRACE_MOTION_EVENTS = false;
103cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
104cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    /**
1059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The system property of dynamic switch for capturing view information
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * when it is set, we dump interested fields and methods for the view on focus
107a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy     */
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String SYSTEM_PROPERTY_CAPTURE_VIEW = "debug.captureview";
109a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The system property of dynamic switch for capturing event information
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * when it is set, we log key events, touch/motion and trackball events
113a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy     */
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent";
115c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
11713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Profiles drawing times in the events log.
11813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
11913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
12013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
12113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
12213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean profileDrawing = false;
12313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
12413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
12513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Profiles layout times in the events log.
12613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
12713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
12813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
12913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
13013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean profileLayout = false;
13113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
13213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
13313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Profiles real fps (times between draws) and displays the result.
13413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
13513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
13613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
13713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
13813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean showFps = false;
13913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
14013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
14113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * <p>Enables or disables views consistency check. Even when this property is enabled,
14213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * view consistency checks happen only if {@link android.util.Config#DEBUG} is set
14313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * to true. The value of this property can be configured externally in one of the
14413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * following files:</p>
14513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * <ul>
14613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/system/debug.prop</li>
14713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/debug.prop</li>
14813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/data/debug.prop</li>
14913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * </ul>
15013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
15113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
15213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
15313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean consistencyCheckEnabled = false;
15413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
15513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    static {
156e551dc76bcde0e114b06e876eeb1d5f9d367fddfRomain Guy        if (Config.DEBUG) {
157e551dc76bcde0e114b06e876eeb1d5f9d367fddfRomain Guy	        Debug.setFieldsOn(ViewDebug.class, true);
158e551dc76bcde0e114b06e876eeb1d5f9d367fddfRomain Guy	    }
15913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    }
16013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
16113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This annotation can be used to mark fields and methods to be dumped by
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the view server. Only non-void methods with no arguments can be annotated
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * by this annotation.
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.FIELD, ElementType.METHOD })
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface ExportedProperty {
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When resolveId is true, and if the annotated field/method return value
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * is an int, the value is converted to an Android's resource name.
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the property's value must be transformed into an Android
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *         resource name, false otherwise
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean resolveId() default false;
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * A mapping can be defined to map int values to specific strings. For
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * instance, View.getVisibility() returns 0, 4 or 8. However, these values
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * actually mean VISIBLE, INVISIBLE and GONE. A mapping can be used to see
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * these human readable values:
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <pre>
1859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @ViewDebug.ExportedProperty(mapping = {
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 0, to = "VISIBLE"),
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 8, to = "GONE")
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * })
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * public int getVisibility() { ...
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <pre>
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An array of int to String mappings
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see android.view.ViewDebug.IntToString
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        IntToString[] mapping() default { };
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
200c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * A mapping can be defined to map array indices to specific strings.
201c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * A mapping can be used to see human readable values for the indices
202c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * of an array:
203c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
204c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * <pre>
205809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @ViewDebug.ExportedProperty(indexMapping = {
206c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 0, to = "INVALID"),
207c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 1, to = "FIRST"),
208c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 2, to = "SECOND")
209c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * })
210c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * private int[] mElements;
211c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * <pre>
212c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
213c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @return An array of int to String mappings
214c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
215c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @see android.view.ViewDebug.IntToString
216c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @see #mapping()
217c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         */
218c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        IntToString[] indexMapping() default { };
219c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
220c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        /**
221809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * A flags mapping can be defined to map flags encoded in an integer to
222809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * specific strings. A mapping can be used to see human readable values
223809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * for the flags of an integer:
224809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
225809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <pre>
226809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @ViewDebug.ExportedProperty(flagMapping = {
227809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *     @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = ENABLED, name = "ENABLED"),
228809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *     @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = DISABLED, name = "DISABLED"),
229809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * })
230809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * private int mFlags;
231809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <pre>
232809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
233809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * A specified String is output when the following is true:
234a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         *
235809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An array of int to String mappings
236809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
237809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        FlagToString[] flagMapping() default { };
238809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
239809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When deep export is turned on, this property is not dumped. Instead, the
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * properties contained in this property are dumped. Each child property
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * is prefixed with the name of this property.
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the properties of this property should be dumped
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
246a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         * @see #prefix()
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean deepExport() default false;
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The prefix to use on child properties when deep export is enabled
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return a prefix as a String
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see #deepExport()
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String prefix() default "";
258bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev
259bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev        /**
260bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         * Specifies the category the property falls into, such as measurement,
261bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         * layout, drawing, etc.
262bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         *
263bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         * @return the category as String
264bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev         */
265bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev        String category() default "";
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines a mapping from an int value to a String. Such a mapping can be used
2709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in a @ExportedProperty to provide more meaningful values to the end user.
2719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see android.view.ViewDebug.ExportedProperty
2739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.TYPE })
2759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
2769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface IntToString {
2779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The original int value to map to a String.
2799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An arbitrary int value.
2819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int from();
2839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The String to use in place of the original int value.
2869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An arbitrary non-null String.
2889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String to();
2909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
291809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
292809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    /**
293809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * Defines a mapping from an flag to a String. Such a mapping can be used
294809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * in a @ExportedProperty to provide more meaningful values to the end user.
295809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     *
296809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * @see android.view.ViewDebug.ExportedProperty
297809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     */
298809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    @Target({ ElementType.TYPE })
299809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    @Retention(RetentionPolicy.RUNTIME)
300809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    public @interface FlagToString {
301809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
302809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The mask to apply to the original value.
303809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
304809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary int value.
305809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
306809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        int mask();
307809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
308809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
309809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The value to compare to the result of:
310809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <code>original value &amp; {@link #mask()}</code>.
311809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
312809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary value.
313809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
314809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        int equals();
315809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
316809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
317809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The String to use in place of the original int value.
318809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
319809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary non-null String.
320809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
321809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        String name();
322809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
323809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
324809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * Indicates whether to output the flag when the test is true,
325809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * or false. Defaults to true.
326809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
327809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        boolean outputIf() default true;
328809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    }
329809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This annotation can be used to mark fields and methods to be dumped when
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the view is captured. Methods with this annotation must have no arguments
333f8a7ceaef2e7d5cd530c9426bde91b6fa9a40b75Andy Stadler     * and must return a valid type of data.
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.FIELD, ElementType.METHOD })
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface CapturedViewProperty {
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
339a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         * When retrieveReturn is true, we need to retrieve second level methods
3409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod()
341a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         * we will set retrieveReturn = true on the annotation of
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * myView.getFirstLevelMethod()
343a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy         * @return true if we need the second level methods
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
345a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy        boolean retrieveReturn() default false;
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
347a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Maximum delay in ms after which we stop trying to capture a View's drawing
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CAPTURE_TIMEOUT = 4000;
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_DUMP = "DUMP";
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
358c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static final String REMOTE_PROFILE = "PROFILE";
359223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Field[]> sFieldsForClasses;
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Method[]> sMethodsForClasses;
363c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
364c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines the type of hierarhcy trace to output to the hierarchy traces file.
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum HierarchyTraceType {
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE,
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE_CHILD,
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE_CHILD_IN_PARENT,
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        REQUEST_LAYOUT,
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ON_LAYOUT,
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ON_MEASURE,
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        DRAW,
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BUILD_CACHE
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static BufferedWriter sHierarchyTraces;
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static ViewRoot sHierarhcyRoot;
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sHierarchyTracePrefix;
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines the type of recycler trace to output to the recycler traces file.
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum RecyclerTraceType {
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        NEW_VIEW,
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BIND_VIEW,
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RECYCLE_FROM_ACTIVE_HEAP,
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RECYCLE_FROM_SCRAP_HEAP,
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_TO_SCRAP_HEAP,
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class RecyclerTrace {
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int view;
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public RecyclerTraceType type;
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int position;
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int indexOnScreen;
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static View sRecyclerOwnerView;
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static List<View> sRecyclerViews;
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static List<RecyclerTrace> sRecyclerTraces;
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sRecyclerTracePrefix;
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
408cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * Defines the type of motion events trace to output to the motion events traces file.
409cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
410cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @hide
411cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     */
412cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    public enum MotionEventTraceType {
413cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        DISPATCH,
414cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        ON_INTERCEPT,
415cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        ON_TOUCH
416cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    }
417cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
418cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    private static BufferedWriter sMotionEventTraces;
419cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    private static ViewRoot sMotionEventRoot;
420cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    private static String sMotionEventTracePrefix;
421cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
422cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    /**
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of instanciated Views.
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The number of Views instanciated in the current process.
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static long getViewInstanceCount() {
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return View.sInstanceCount;
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of instanciated ViewRoots.
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The number of ViewRoots instanciated in the current process.
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static long getViewRootInstanceCount() {
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ViewRoot.getInstanceCount();
442a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy    }
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Outputs a trace to the currently opened recycler traces. The trace records the type of
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * recycler action performed on the supplied view as well as a number of parameters.
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view to trace
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param type the type of the trace
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param parameters parameters depending on the type of the trace
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void trace(View view, RecyclerTraceType type, int... parameters) {
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!sRecyclerViews.contains(view)) {
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sRecyclerViews.add(view);
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int index = sRecyclerViews.indexOf(view);
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RecyclerTrace trace = new RecyclerTrace();
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.view = index;
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.type = type;
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.position = parameters[0];
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.indexOnScreen = parameters[1];
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces.add(trace);
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Starts tracing the view recycler of the specified view. The trace is identified by a prefix,
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used to build the traces files names: <code>/EXTERNAL/view-recycler/PREFIX.traces</code> and
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>.
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one view recycler can be traced at the same time. After calling this method, any
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * other invocation will result in a <code>IllegalStateException</code> unless
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #stopRecyclerTracing()} is invoked before.
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Traces files are created only after {@link #stopRecyclerTracing()} is invoked.
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_RECYCLER is false.
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param prefix the traces files name prefix
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view whose recycler must be traced
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #stopRecyclerTracing()
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void startRecyclerTracing(String prefix, View view) {
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_RECYCLER) {
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView != null) {
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call stopRecyclerTracing() before running" +
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " a new trace!");
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTracePrefix = prefix;
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerOwnerView = view;
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews = new ArrayList<View>();
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces = new LinkedList<RecyclerTrace>();
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stops the current view recycer tracing.
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.traces</code>
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all the traces (or method calls) relative to the specified view's recycler.
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all of the views used by the recycler of the view supplied to
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #startRecyclerTracing(String, View)}.
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_RECYCLER is false.
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #startRecyclerTracing(String, View)
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void stopRecyclerTracing() {
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_RECYCLER) {
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call startRecyclerTracing() before" +
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " stopRecyclerTracing()!");
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
535c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump.mkdirs();
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler");
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final BufferedWriter out = new BufferedWriter(new FileWriter(recyclerDump), 8 * 1024);
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (View view : sRecyclerViews) {
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final String name = view.getClass().getName();
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(name);
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.newLine();
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.close();
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump recycler content");
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5571afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn            if (recyclerDump.exists()) {
5581afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn                recyclerDump.delete();
5591afd1c90ebe789b8d3a137004127a50d2db7e3b5Dianne Hackborn            }
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final FileOutputStream file = new FileOutputStream(recyclerDump);
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final DataOutputStream out = new DataOutputStream(file);
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (RecyclerTrace trace : sRecyclerTraces) {
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.view);
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.type.ordinal());
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.position);
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.indexOnScreen);
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.flush();
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.close();
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump recycler traces");
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews.clear();
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews = null;
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces.clear();
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces = null;
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerOwnerView = null;
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Outputs a trace to the currently opened traces file. The trace contains the class name
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and instance's hashcode of the specified view as well as the supplied trace type.
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view to trace
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param type the type of the trace
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void trace(View view, HierarchyTraceType type) {
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarchyTraces == null) {
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(type.name());
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(' ');
6019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(view.getClass().getName());
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write('@');
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(Integer.toHexString(view.hashCode()));
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.newLine();
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping trace of type " + type + " for view " + view);
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Starts tracing the view hierarchy of the specified view. The trace is identified by a prefix,
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used to build the traces files names: <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code> and
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>.
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one view hierarchy can be traced at the same time. After calling this method, any
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * other invocation will result in a <code>IllegalStateException</code> unless
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #stopHierarchyTracing()} is invoked before.
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all the traces (or method calls) relative to the specified view's hierarchy.
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_HIERARCHY is false.
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param prefix the traces files name prefix
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view whose hierarchy must be traced
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #stopHierarchyTracing()
6289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
6299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void startHierarchyTracing(String prefix, View view) {
6319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
6329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_HIERARCHY) {
6339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarhcyRoot != null) {
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " a new trace!");
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
642c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump.mkdirs();
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump = new File(hierarchyDump, prefix + ".traces");
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarchyTracePrefix = prefix;
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
6499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump view hierarchy");
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarhcyRoot = (ViewRoot) view.getRootView().getParent();
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stops the current view hierarchy tracing. This method closes the file
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>.
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing the view hierarchy of the view supplied to
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #startHierarchyTracing(String, View)}.
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_HIERARCHY is false.
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
668a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy     * @see #startHierarchyTracing(String, View)
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void stopHierarchyTracing() {
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_HIERARCHY) {
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarhcyRoot == null || sHierarchyTraces == null) {
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call startHierarchyTracing() before" +
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " stopHierarchyTracing()!");
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.close();
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not write view traces");
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarchyTraces = null;
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
690c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
6919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump.mkdirs();
6929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree");
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedWriter out;
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
6979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
6989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump view hierarchy");
6999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
7009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        View view = sHierarhcyRoot.getView();
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view instanceof ViewGroup) {
7049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ViewGroup group = (ViewGroup) view;
7059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dumpViewHierarchy(group, out, 0);
7069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
7079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
7089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
7099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e("View", "Could not dump view hierarchy");
7109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
7119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarhcyRoot = null;
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
715a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
716cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    /**
717cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * Outputs a trace to the currently opened traces file. The trace contains the class name
718cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * and instance's hashcode of the specified view as well as the supplied trace type.
719cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
720cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @param view the view to trace
721cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @param event the event of the trace
722cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @param type the type of the trace
723cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
724cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @hide
725cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     */
726cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    public static void trace(View view, MotionEvent event, MotionEventTraceType type) {
727cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        if (sMotionEventTraces == null) {
728cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            return;
729cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
730cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
731cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        try {
732cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces.write(type.name());
733cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces.write(' ');
734cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces.write(event.getAction());
735cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces.write(' ');
736cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces.write(view.getClass().getName());
737cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces.write('@');
738cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces.write(Integer.toHexString(view.hashCode()));
739cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sHierarchyTraces.newLine();
740cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        } catch (IOException e) {
741cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            Log.w("View", "Error while dumping trace of event " + event + " for view " + view);
742cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
743cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    }
744cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
745cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    /**
746cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * Starts tracing the motion events for the hierarchy of the specificy view.
747cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * The trace is identified by a prefix, used to build the traces files names:
748cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * <code>/EXTERNAL/motion-events/PREFIX.traces</code> and
749cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * <code>/EXTERNAL/motion-events/PREFIX.tree</code>.
750cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
751cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * Only one view hierarchy can be traced at the same time. After calling this method, any
752cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * other invocation will result in a <code>IllegalStateException</code> unless
753cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * {@link #stopMotionEventTracing()} is invoked before.
754cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
755cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * Calling this method creates the file <code>/EXTERNAL/motion-events/PREFIX.traces</code>
756cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * containing all the traces (or method calls) relative to the specified view's hierarchy.
757cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
758cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * This method will return immediately if TRACE_HIERARCHY is false.
759cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
760cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @param prefix the traces files name prefix
761cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @param view the view whose hierarchy must be traced
762cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
763cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @see #stopMotionEventTracing()
764cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @see #trace(View, MotionEvent, android.view.ViewDebug.MotionEventTraceType)
765cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
766cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @hide
767cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     */
768cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    public static void startMotionEventTracing(String prefix, View view) {
769cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        //noinspection PointlessBooleanExpression,ConstantConditions
770cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        if (!TRACE_MOTION_EVENTS) {
771cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            return;
772cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
773cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
774cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        if (sMotionEventRoot != null) {
775cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            throw new IllegalStateException("You must call stopMotionEventTracing() before running" +
776cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy                " a new trace!");
777cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
778cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
779cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "motion-events/");
780cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        //noinspection ResultOfMethodCallIgnored
781cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        hierarchyDump.mkdirs();
782cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
783cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        hierarchyDump = new File(hierarchyDump, prefix + ".traces");
784cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        sMotionEventTracePrefix = prefix;
785cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
786cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        try {
787cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces = new BufferedWriter(new FileWriter(hierarchyDump), 32 * 1024);
788cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        } catch (IOException e) {
789cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            Log.e("View", "Could not dump view hierarchy");
790cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            return;
791cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
792cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
793cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        sMotionEventRoot = (ViewRoot) view.getRootView().getParent();
794cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    }
795cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
796cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    /**
797cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * Stops the current motion events tracing. This method closes the file
798cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * <code>/EXTERNAL/motion-events/PREFIX.traces</code>.
799cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
800cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * Calling this method creates the file <code>/EXTERNAL/motion-events/PREFIX.tree</code>
801cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * containing the view hierarchy of the view supplied to
802cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * {@link #startMotionEventTracing(String, View)}.
803cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
804cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * This method will return immediately if TRACE_HIERARCHY is false.
805cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
806cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @see #startMotionEventTracing(String, View)
807cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @see #trace(View, MotionEvent, android.view.ViewDebug.MotionEventTraceType)
808cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     *
809cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     * @hide
810cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy     */
811cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    public static void stopMotionEventTracing() {
812cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        //noinspection PointlessBooleanExpression,ConstantConditions
813cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        if (!TRACE_MOTION_EVENTS) {
814cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            return;
815cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
816cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
817cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        if (sMotionEventRoot == null || sMotionEventTraces == null) {
818cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            throw new IllegalStateException("You must call startMotionEventTracing() before" +
819cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy                " stopMotionEventTracing()!");
820cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
821cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
822cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        try {
823cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            sMotionEventTraces.close();
824cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        } catch (IOException e) {
825cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            Log.e("View", "Could not write view traces");
826cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
827cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        sMotionEventTraces = null;
828cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
829cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "motion-events/");
830cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        //noinspection ResultOfMethodCallIgnored
831cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        hierarchyDump.mkdirs();
832cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        hierarchyDump = new File(hierarchyDump, sMotionEventTracePrefix + ".tree");
833cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
834cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        BufferedWriter out;
835cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        try {
836cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
837cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        } catch (IOException e) {
838cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            Log.e("View", "Could not dump view hierarchy");
839cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            return;
840cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
841cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
842cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        View view = sMotionEventRoot.getView();
843cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        if (view instanceof ViewGroup) {
844cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            ViewGroup group = (ViewGroup) view;
845cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            dumpViewHierarchy(group, out, 0);
846cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            try {
847cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy                out.close();
848cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            } catch (IOException e) {
849cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy                Log.e("View", "Could not dump view hierarchy");
850cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy            }
851cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        }
852cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
853cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy        sHierarhcyRoot = null;
854cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy    }
855cf635ae7c65de34f62a41cd330f25f4ee9ddc95bRomain Guy
8569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void dispatchCommand(View view, String command, String parameters,
8579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            OutputStream clientStream) throws IOException {
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Paranoid but safe...
8609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        view = view.getRootView();
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dump(view, clientStream);
864223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        } else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
865223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            captureLayers(view, new DataOutputStream(clientStream));
8669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
8679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final String[] params = parameters.split(" ");
8689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
8699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                capture(view, clientStream, params[0]);
8709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (REMOTE_COMMAND_INVALIDATE.equalsIgnoreCase(command)) {
8719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                invalidate(view, params[0]);
8729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) {
8739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                requestLayout(view, params[0]);
874c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } else if (REMOTE_PROFILE.equalsIgnoreCase(command)) {
875c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                profile(view, clientStream, params[0]);
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
880c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static View findView(View root, String parameter) {
881c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        // Look by type/hashcode
882c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (parameter.indexOf('@') != -1) {
883c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final String[] ids = parameter.split("@");
884c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final String className = ids[0];
885236092a36216c79507ec19eb207831810caced19Romain Guy            final int hashCode = (int) Long.parseLong(ids[1], 16);
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
887c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            View view = root.getRootView();
888c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (view instanceof ViewGroup) {
889c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                return findView((ViewGroup) view, className, hashCode);
890c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
891c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
892c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            // Look by id
893c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int id = root.getResources().getIdentifier(parameter, null, null);
894c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            return root.getRootView().findViewById(id);
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
8969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
8989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void invalidate(View root, String parameter) {
901c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
9029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            view.postInvalidate();
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void requestLayout(View root, String parameter) {
908c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            root.post(new Runnable() {
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                public void run() {
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    view.requestLayout();
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            });
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
918c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void profile(View root, OutputStream clientStream, String parameter)
9199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws IOException {
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
921c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
922c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        BufferedWriter out = null;
923c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        try {
924c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
925c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
926c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (view != null) {
927f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev                profileViewAndChildren(view, out);
928f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            } else {
929f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev                out.write("-1 -1 -1");
930f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev                out.newLine();
931f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            }
932f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            out.write("DONE.");
933f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            out.newLine();
934f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        } catch (Exception e) {
935f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            android.util.Log.w("View", "Problem profiling the view:", e);
936f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        } finally {
937f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            if (out != null) {
938f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev                out.close();
939f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            }
940f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        }
941f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev    }
942c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
943f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev    private static void profileViewAndChildren(final View view, BufferedWriter out)
944f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            throws IOException {
945c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev        profileViewAndChildren(view, out, true);
946c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev    }
947f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev
948c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev    private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
949c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev            throws IOException {
950f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev
951c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev        long durationMeasure =
952c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                (root || (view.mPrivateFlags & View.MEASURED_DIMENSION_SET) != 0) ? profileViewOperation(
953c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        view, new ViewOperation<Void>() {
954c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public Void[] pre() {
955c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                forceLayout(view);
956c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                return null;
957c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
958c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
959c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            private void forceLayout(View view) {
960c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                view.forceLayout();
961c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                if (view instanceof ViewGroup) {
962c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                    ViewGroup group = (ViewGroup) view;
963c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                    final int count = group.getChildCount();
964c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                    for (int i = 0; i < count; i++) {
965c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                        forceLayout(group.getChildAt(i));
966c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                    }
967c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                }
968c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
969c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
970c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void run(Void... data) {
971c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
972c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
973c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
974c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void post(Void... data) {
975c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
976c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        })
977c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        : 0;
978c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev        long durationLayout =
979c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                (root || (view.mPrivateFlags & View.LAYOUT_REQUIRED) != 0) ? profileViewOperation(
980c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        view, new ViewOperation<Void>() {
981c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public Void[] pre() {
982c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                return null;
983c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
984c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
985c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void run(Void... data) {
986c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
987c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
988c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
989c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void post(Void... data) {
990c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
991c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        }) : 0;
992c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev        long durationDraw =
993bef337f516eafaee8f3891e22688b74388570317Konstantin Lopyrev                (root || !view.willNotDraw() || (view.mPrivateFlags & View.DRAWN) != 0) ? profileViewOperation(
994bef337f516eafaee8f3891e22688b74388570317Konstantin Lopyrev                        view,
995c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        new ViewOperation<Object>() {
996c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public Object[] pre() {
997c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                final DisplayMetrics metrics =
998c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                        view.getResources().getDisplayMetrics();
999c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                final Bitmap bitmap =
1000c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                        Bitmap.createBitmap(metrics.widthPixels,
1001c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                                metrics.heightPixels, Bitmap.Config.RGB_565);
1002c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                final Canvas canvas = new Canvas(bitmap);
1003c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                return new Object[] {
1004c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                        bitmap, canvas
1005c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                };
1006c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1007c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1008c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void run(Object... data) {
1009c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                view.draw((Canvas) data[1]);
1010c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1011c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev
1012c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            public void post(Object... data) {
1013c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                                ((Bitmap) data[0]).recycle();
1014c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                            }
1015c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                        }) : 0;
1016f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(String.valueOf(durationMeasure));
1017f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(' ');
1018f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(String.valueOf(durationLayout));
1019f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(' ');
1020f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.write(String.valueOf(durationDraw));
1021f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        out.newLine();
1022f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev        if (view instanceof ViewGroup) {
1023f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            ViewGroup group = (ViewGroup) view;
1024f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            final int count = group.getChildCount();
1025f8e1219cf8992a22d6c48c2c70d4fbbccc2494f6Konstantin Lopyrev            for (int i = 0; i < count; i++) {
1026c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                profileViewAndChildren(group.getChildAt(i), out, false);
1027c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1028c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1029c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1030c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1031c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    interface ViewOperation<T> {
1032c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        T[] pre();
1033c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        void run(T... data);
1034c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        void post(T... data);
1035c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1036c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1037c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) {
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final CountDownLatch latch = new CountDownLatch(1);
1039c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final long[] duration = new long[1];
1040c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1041c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        view.post(new Runnable() {
1042c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            public void run() {
1043c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                try {
1044c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    T[] data = operation.pre();
1045c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    long start = Debug.threadCpuTimeNanos();
1046c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    operation.run(data);
1047c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    duration[0] = Debug.threadCpuTimeNanos() - start;
1048c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    operation.post(data);
1049c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } finally {
1050c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    latch.countDown();
1051c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1052c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1053c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        });
1054c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1055c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        try {
1056c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev            if (!latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS)) {
1057c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                Log.w("View", "Could not complete the profiling of the view " + view);
1058c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev                return -1;
1059c6dc45700bf0c18708b0ad2f695ea85fadcbf131Konstantin Lopyrev            }
1060c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (InterruptedException e) {
1061c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            Log.w("View", "Could not complete the profiling of the view " + view);
1062c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            Thread.currentThread().interrupt();
1063c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            return -1;
1064c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1065c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1066c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        return duration[0];
1067c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1068c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1069223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    private static void captureLayers(View root, final DataOutputStream clientStream)
1070223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            throws IOException {
1071223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1072223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        try {
1073223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            Rect outRect = new Rect();
1074223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            try {
1075223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                root.mAttachInfo.mSession.getDisplayFrame(root.mAttachInfo.mWindow, outRect);
1076223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            } catch (RemoteException e) {
1077223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                // Ignore
1078223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            }
1079223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1080223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeInt(outRect.width());
1081223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeInt(outRect.height());
1082223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
108365554f27855ce1764123604b061b10346f8b8404Romain Guy            captureViewLayer(root, clientStream, true);
1084223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1085223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.write(2);
1086223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        } finally {
1087223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.close();
1088223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        }
1089223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    }
1090223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
109165554f27855ce1764123604b061b10346f8b8404Romain Guy    private static void captureViewLayer(View view, DataOutputStream clientStream, boolean visible)
1092223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            throws IOException {
1093223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
109465554f27855ce1764123604b061b10346f8b8404Romain Guy        final boolean localVisible = view.getVisibility() == View.VISIBLE && visible;
109565554f27855ce1764123604b061b10346f8b8404Romain Guy
1096223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        if ((view.mPrivateFlags & View.SKIP_DRAW) != View.SKIP_DRAW) {
1097223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            final int id = view.getId();
1098223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            String name = view.getClass().getSimpleName();
1099223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            if (id != View.NO_ID) {
1100223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                name = resolveId(view.getContext(), id).toString();
1101223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            }
1102223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1103223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.write(1);
1104223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeUTF(name);
110565554f27855ce1764123604b061b10346f8b8404Romain Guy            clientStream.writeByte(localVisible ? 1 : 0);
1106223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1107223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            int[] position = new int[2];
1108223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            // XXX: Should happen on the UI thread
1109223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            view.getLocationInWindow(position);
1110223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1111223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeInt(position[0]);
1112223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.writeInt(position[1]);
1113223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.flush();
1114223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1115223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            Bitmap b = performViewCapture(view, true);
1116223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            if (b != null) {
1117223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                ByteArrayOutputStream arrayOut = new ByteArrayOutputStream(b.getWidth() *
1118223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                        b.getHeight() * 2);
1119223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                b.compress(Bitmap.CompressFormat.PNG, 100, arrayOut);
1120223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                clientStream.writeInt(arrayOut.size());
1121223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                arrayOut.writeTo(clientStream);
1122223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            }
1123223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            clientStream.flush();
1124223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        }
1125223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1126223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        if (view instanceof ViewGroup) {
1127223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            ViewGroup group = (ViewGroup) view;
1128223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            int count = group.getChildCount();
1129223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1130223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            for (int i = 0; i < count; i++) {
113165554f27855ce1764123604b061b10346f8b8404Romain Guy                captureViewLayer(group.getChildAt(i), clientStream, localVisible);
1132223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            }
1133223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        }
1134223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    }
1135223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1136c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void capture(View root, final OutputStream clientStream, String parameter)
1137c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            throws IOException {
1138c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1139c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View captureView = findView(root, parameter);
1140223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        Bitmap b = performViewCapture(captureView, false);
114143b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev
114243b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        if (b == null) {
1143223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            Log.w("View", "Failed to create capture bitmap!");
114443b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            // Send an empty one so that it doesn't get stuck waiting for
114543b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            // something.
114643b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
114743b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        }
114843b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev
114943b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        BufferedOutputStream out = null;
115043b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        try {
115143b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            out = new BufferedOutputStream(clientStream, 32 * 1024);
115243b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            b.compress(Bitmap.CompressFormat.PNG, 100, out);
115343b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            out.flush();
115443b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev        } finally {
115543b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            if (out != null) {
115643b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev                out.close();
115743b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            }
115843b9b48f792e0b1b166af7f7fb6a32172e7f11c2Konstantin Lopyrev            b.recycle();
1159223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        }
1160223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    }
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1162223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy    private static Bitmap performViewCapture(final View captureView, final boolean skpiChildren) {
11639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (captureView != null) {
1164c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final CountDownLatch latch = new CountDownLatch(1);
11659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Bitmap[] cache = new Bitmap[1];
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1167223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy            captureView.post(new Runnable() {
11689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                public void run() {
11699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
1170958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                        cache[0] = captureView.createSnapshot(
1171223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                                Bitmap.Config.ARGB_8888, 0, skpiChildren);
1172958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                    } catch (OutOfMemoryError e) {
1173958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                        try {
1174958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                            cache[0] = captureView.createSnapshot(
1175223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                                    Bitmap.Config.ARGB_4444, 0, skpiChildren);
1176958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                        } catch (OutOfMemoryError e2) {
1177958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                            Log.w("View", "Out of memory for bitmap");
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } finally {
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        latch.countDown();
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            });
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
1187223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy                return cache[0];
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (InterruptedException e) {
1189c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                Log.w("View", "Could not complete the capture of the view " + captureView);
1190c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                Thread.currentThread().interrupt();
11919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1193223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy
1194223ff5c0586adbbd1d6d57a3a4d176222e8b7434Romain Guy        return null;
11959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dump(View root, OutputStream clientStream) throws IOException {
11989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedWriter out = null;
11999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
120038e951b62ed956501b11f0715e23e3bf70942f6dRomain Guy            out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            View view = root.getRootView();
12029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ViewGroup group = (ViewGroup) view;
1204105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewHierarchyWithProperties(group.getContext(), group, out, 0);
12059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write("DONE.");
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
1208c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (Exception e) {
1209c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            android.util.Log.w("View", "Problem dumping the view:", e);
12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (out != null) {
12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static View findView(ViewGroup group, String className, int hashCode) {
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isRequestedView(group, className, hashCode)) {
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return group;
12209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
12239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
12249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
12259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
12269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final View found = findView((ViewGroup) view, className, hashCode);
12279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (found != null) {
12289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return found;
12299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (isRequestedView(view, className, hashCode)) {
12319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return view;
12329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
12369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean isRequestedView(View view, String className, int hashCode) {
12399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
12409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1242105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewHierarchyWithProperties(Context context, ViewGroup group,
12439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            BufferedWriter out, int level) {
1244105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (!dumpViewWithProperties(context, group, out, level)) {
12459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
12469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
12499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
12509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
12519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
1252105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewHierarchyWithProperties(context, (ViewGroup) view, out, level + 1);
12539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
1254105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewWithProperties(context, view, out, level + 1);
12559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1259105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static boolean dumpViewWithProperties(Context context, View view,
1260105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out, int level) {
1261105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
12629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
12639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < level; i++) {
12649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(' ');
12659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(view.getClass().getName());
12679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write('@');
12689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(Integer.toHexString(view.hashCode()));
12699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(' ');
1270105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            dumpViewProperties(context, view, out);
12719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
12729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
12739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping hierarchy tree");
12749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
12759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
12779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Field[] getExportedPropertyFields(Class<?> klass) {
12809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sFieldsForClasses == null) {
12819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sFieldsForClasses = new HashMap<Class<?>, Field[]>();
12829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1283c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (sAnnotations == null) {
1284c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
1285c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1286c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
12879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
1288c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
12899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Field[] fields = map.get(klass);
12919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fields != null) {
12929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fields;
12939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Field> foundFields = new ArrayList<Field>();
12969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = klass.getDeclaredFields();
12979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
12999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
13009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
13019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (field.isAnnotationPresent(ExportedProperty.class)) {
13029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                field.setAccessible(true);
13039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundFields.add(field);
1304c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                annotations.put(field, field.getAnnotation(ExportedProperty.class));
13059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = foundFields.toArray(new Field[foundFields.size()]);
13099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, fields);
13109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fields;
13129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Method[] getExportedPropertyMethods(Class<?> klass) {
13159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sMethodsForClasses == null) {
1316c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
1317c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1318c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (sAnnotations == null) {
1319c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
13209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1321c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
13229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
1323c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
13249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Method[] methods = map.get(klass);
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (methods != null) {
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return methods;
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Method> foundMethods = new ArrayList<Method>();
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = klass.getDeclaredMethods();
1332a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
1335a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy            final Method method = methods[i];
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (method.getParameterTypes().length == 0 &&
1337c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    method.isAnnotationPresent(ExportedProperty.class) &&
1338c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    method.getReturnType() != Void.class) {
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                method.setAccessible(true);
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundMethods.add(method);
1341c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                annotations.put(method, method.getAnnotation(ExportedProperty.class));
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = foundMethods.toArray(new Method[foundMethods.size()]);
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, methods);
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return methods;
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1351105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewProperties(Context context, Object view,
1352105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out) throws IOException {
1353105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1354105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        dumpViewProperties(context, view, out, "");
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1357105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewProperties(Context context, Object view,
1358105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out, String prefix) throws IOException {
1359105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> klass = view.getClass();
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        do {
1363105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            exportFields(context, view, out, klass, prefix);
1364105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            exportMethods(context, view, out, klass, prefix);
13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            klass = klass.getSuperclass();
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } while (klass != Object.class);
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1368a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
1369105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportMethods(Context context, Object view, BufferedWriter out,
1370105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            Class<?> klass, String prefix) throws IOException {
13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Method[] methods = getExportedPropertyMethods(klass);
13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection EmptyCatchBlock
13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // TODO: This should happen on the UI thread
13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object methodValue = method.invoke(view, (Object[]) null);
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> returnType = method.getReturnType();
1382bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev                final ExportedProperty property = sAnnotations.get(method);
138391a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                String categoryPrefix =
138491a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                        property.category().length() != 0 ? property.category() + ":" : "";
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (returnType == int.class) {
1387bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev
1388105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    if (property.resolveId() && context != null) {
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final int id = (Integer) methodValue;
1390105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        methodValue = resolveId(context, id);
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1392809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        final FlagToString[] flagsMapping = property.flagMapping();
1393809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        if (flagsMapping.length > 0) {
1394809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            final int intValue = (Integer) methodValue;
139591a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                            final String valuePrefix =
139691a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                                    categoryPrefix + prefix + method.getName() + '_';
1397809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
1398809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        }
1399809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final IntToString[] mapping = property.mapping();
14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mapping.length > 0) {
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            final int intValue = (Integer) methodValue;
14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            boolean mapped = false;
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            int mappingCount = mapping.length;
14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            for (int j = 0; j < mappingCount; j++) {
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                final IntToString mapper = mapping[j];
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                if (mapper.from() == intValue) {
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    methodValue = mapper.to();
14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    mapped = true;
14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    break;
14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                }
14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (!mapped) {
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                methodValue = intValue;
14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1419c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } else if (returnType == int[].class) {
1420c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final int[] array = (int[]) methodValue;
142191a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                    final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
1422c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String suffix = "()";
1423c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1424105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
1425bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev
1426bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev                    // Probably want to return here, same as for fields.
1427bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev                    return;
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!returnType.isPrimitive()) {
14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.deepExport()) {
1430105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        dumpViewProperties(context, methodValue, out, prefix + property.prefix());
14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
14339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
143591a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (InvocationTargetException e) {
14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1442105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportFields(Context context, Object view, BufferedWriter out,
1443105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            Class<?> klass, String prefix) throws IOException {
1444105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Field[] fields = getExportedPropertyFields(klass);
14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection EmptyCatchBlock
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object fieldValue = null;
14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> type = field.getType();
1455bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev                final ExportedProperty property = sAnnotations.get(field);
145691a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                String categoryPrefix =
145791a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                        property.category().length() != 0 ? property.category() + ":" : "";
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (type == int.class) {
1460bea95162ca25bd00b0479d93739b6283795c3986Konstantin Lopyrev
1461105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    if (property.resolveId() && context != null) {
14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final int id = field.getInt(view);
1463105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        fieldValue = resolveId(context, id);
14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1465809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        final FlagToString[] flagsMapping = property.flagMapping();
1466809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        if (flagsMapping.length > 0) {
1467809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            final int intValue = field.getInt(view);
146891a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                            final String valuePrefix =
146991a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                                    categoryPrefix + prefix + field.getName() + '_';
1470809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
1471809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        }
1472809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final IntToString[] mapping = property.mapping();
14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mapping.length > 0) {
14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            final int intValue = field.getInt(view);
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            int mappingCount = mapping.length;
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            for (int j = 0; j < mappingCount; j++) {
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                final IntToString mapped = mapping[j];
14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                if (mapped.from() == intValue) {
14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    fieldValue = mapped.to();
14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    break;
14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                }
14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (fieldValue == null) {
14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                fieldValue = intValue;
14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1490c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } else if (type == int[].class) {
1491c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final int[] array = (int[]) field.get(view);
149291a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                    final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
1493c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String suffix = "";
1494c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1495105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
1496c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1497c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    // We exit here!
1498c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    return;
14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!type.isPrimitive()) {
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.deepExport()) {
150191a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                        dumpViewProperties(context, field.get(view), out, prefix
150291a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                                + property.prefix());
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fieldValue == null) {
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fieldValue = field.get(view);
15099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
15109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
151191a7f5fef8a9fc496acef23f513cb48139e8dff5Konstantin Lopyrev                writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
15129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
15139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
15149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
15159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1517c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void writeEntry(BufferedWriter out, String prefix, String name,
1518c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String suffix, Object value) throws IOException {
1519c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1520c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(prefix);
1521c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(name);
1522c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(suffix);
1523c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write("=");
1524c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        writeValue(out, value);
1525c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(' ');
1526c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1527c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1528809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    private static void exportUnrolledFlags(BufferedWriter out, FlagToString[] mapping,
1529809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            int intValue, String prefix) throws IOException {
1530809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
1531809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        final int count = mapping.length;
1532809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        for (int j = 0; j < count; j++) {
1533809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            final FlagToString flagMapping = mapping[j];
1534809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            final boolean ifTrue = flagMapping.outputIf();
15355bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy            final int maskResult = intValue & flagMapping.mask();
15365bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy            final boolean test = maskResult == flagMapping.equals();
1537809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            if ((test && ifTrue) || (!test && !ifTrue)) {
1538809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                final String name = flagMapping.name();
15395bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy                final String value = "0x" + Integer.toHexString(maskResult);
1540809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                writeEntry(out, prefix, name, "", value);
1541809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            }
1542809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        }
1543809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    }
1544809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
1545105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportUnrolledArray(Context context, BufferedWriter out,
1546c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            ExportedProperty property, int[] array, String prefix, String suffix)
1547c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            throws IOException {
1548c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1549c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final IntToString[] indexMapping = property.indexMapping();
1550c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean hasIndexMapping = indexMapping.length > 0;
1551c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1552c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final IntToString[] mapping = property.mapping();
1553c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean hasMapping = mapping.length > 0;
1554c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1555105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        final boolean resolveId = property.resolveId() && context != null;
1556c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final int valuesCount = array.length;
1557c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1558c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        for (int j = 0; j < valuesCount; j++) {
1559c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String name;
1560a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy            String value = null;
1561c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1562c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int intValue = array[j];
1563c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1564c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            name = String.valueOf(j);
1565c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (hasIndexMapping) {
1566c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                int mappingCount = indexMapping.length;
1567c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                for (int k = 0; k < mappingCount; k++) {
1568c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final IntToString mapped = indexMapping[k];
1569c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    if (mapped.from() == j) {
1570c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        name = mapped.to();
1571c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        break;
1572c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
1573c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1574c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1575c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1576c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (hasMapping) {
1577c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                int mappingCount = mapping.length;
1578c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                for (int k = 0; k < mappingCount; k++) {
1579c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final IntToString mapped = mapping[k];
1580c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    if (mapped.from() == intValue) {
1581c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        value = mapped.to();
1582c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        break;
1583c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
1584c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1585c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1586c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1587c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (resolveId) {
1588a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                if (value == null) value = (String) resolveId(context, intValue);
1589a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy            } else {
1590a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                value = String.valueOf(intValue);
1591c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1592c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1593c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            writeEntry(out, prefix, name, suffix, value);
1594c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1595c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1596c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1597237c1ceea36024cf4194212e713806e3ce8a1c49Romain Guy    static Object resolveId(Context context, int id) {
1598c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        Object fieldValue;
1599105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        final Resources resources = context.getResources();
1600c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (id >= 0) {
1601c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            try {
1602c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                fieldValue = resources.getResourceTypeName(id) + '/' +
1603c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        resources.getResourceEntryName(id);
1604c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } catch (Resources.NotFoundException e) {
1605c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                fieldValue = "id/0x" + Integer.toHexString(id);
1606c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1607c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
1608c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            fieldValue = "NO_ID";
1609c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1610c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        return fieldValue;
1611c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1612c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1613c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void writeValue(BufferedWriter out, Object value) throws IOException {
1614c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (value != null) {
1615c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String output = value.toString().replace("\n", "\\n");
1616c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(String.valueOf(output.length()));
1617c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(",");
1618c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(output);
1619c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
1620c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write("4,null");
1621c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1622c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1623c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
16249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dumpViewHierarchy(ViewGroup group, BufferedWriter out, int level) {
16259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!dumpView(group, out, level)) {
16269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
16279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
16309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
16319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
16329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
16339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpViewHierarchy((ViewGroup) view, out, level + 1);
16349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
16359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpView(view, out, level + 1);
16369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean dumpView(Object view, BufferedWriter out, int level) {
16419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
16429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < level; i++) {
16439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(' ');
16449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(view.getClass().getName());
16469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write('@');
16479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(Integer.toHexString(view.hashCode()));
16489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
16499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
16509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping hierarchy tree");
16519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
16529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
16549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
16579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mCapturedViewFieldsForClasses == null) {
16589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
16599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
16619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Field[] fields = map.get(klass);
16639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fields != null) {
16649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fields;
16659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Field> foundFields = new ArrayList<Field>();
16689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = klass.getFields();
16699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
16719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
16729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
16739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (field.isAnnotationPresent(CapturedViewProperty.class)) {
16749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                field.setAccessible(true);
16759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundFields.add(field);
16769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
16779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = foundFields.toArray(new Field[foundFields.size()]);
16809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, fields);
16819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fields;
16839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
16849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
16869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mCapturedViewMethodsForClasses == null) {
16879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
16889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
16909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Method[] methods = map.get(klass);
16929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (methods != null) {
16939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return methods;
16949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
16959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
16969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Method> foundMethods = new ArrayList<Method>();
16979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = klass.getMethods();
1698a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
16999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
17009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
1701a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy            final Method method = methods[i];
17029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (method.getParameterTypes().length == 0 &&
17039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    method.isAnnotationPresent(CapturedViewProperty.class) &&
17049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    method.getReturnType() != Void.class) {
17059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                method.setAccessible(true);
17069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundMethods.add(method);
17079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = foundMethods.toArray(new Method[foundMethods.size()]);
17119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, methods);
17129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return methods;
17149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1715a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
1716a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy    private static String capturedViewExportMethods(Object obj, Class<?> klass,
17179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String prefix) {
17189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (obj == null) {
17209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "null";
17219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1722a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
17239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
17249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Method[] methods = capturedViewGetPropertyMethods(klass);
17259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
17279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
17289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
17299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
17309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object methodValue = method.invoke(obj, (Object[]) null);
17319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> returnType = method.getReturnType();
1732a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
17339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
17349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (property.retrieveReturn()) {
17359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    //we are interested in the second level data only
17369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
1737a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                } else {
17389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(prefix);
17399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(method.getName());
17409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("()=");
1741a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
17429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (methodValue != null) {
1743a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                        final String value = methodValue.toString().replace("\n", "\\n");
1744a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                        sb.append(value);
17459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
17469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sb.append("null");
17479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
17489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("; ");
17499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
17509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              } catch (IllegalAccessException e) {
1751a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                  //Exception IllegalAccess, it is OK here
17529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //we simply ignore this method
17539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              } catch (InvocationTargetException e) {
1754a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                  //Exception InvocationTarget, it is OK here
17559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //we simply ignore this method
1756a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy              }
1757a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy        }
17589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
17599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
17609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
1762a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
17639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (obj == null) {
17649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "null";
17659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1766a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
17679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
17689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Field[] fields = capturedViewGetPropertyFields(klass);
17699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
17719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
17729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
17739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
17749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object fieldValue = field.get(obj);
17759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(prefix);
17779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(field.getName());
17789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("=");
17799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
17809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fieldValue != null) {
17819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    final String value = fieldValue.toString().replace("\n", "\\n");
17829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(value);
17839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
17849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("null");
17859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
17869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(' ');
17879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
1788a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy                //Exception IllegalAccess, it is OK here
17899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                //we simply ignore this field
17909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
17919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
17929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
17939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1794a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy
17959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1796a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy     * Dump view info for id based instrument test generation
17979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (and possibly further data analysis). The results are dumped
1798a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy     * to the log.
17999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag for log
18009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view for dump
18019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1802a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy    public static void dumpCapturedView(String tag, Object view) {
18039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> klass = view.getClass();
18049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder(klass.getName() + ": ");
18059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sb.append(capturedViewExportFields(view, klass, ""));
1806a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy        sb.append(capturedViewExportMethods(view, klass, ""));
1807a1f3e4aef19882b4b81075d9205bd363efe1e66dRomain Guy        Log.d(tag, sb.toString());
18089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
18099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1810