ViewDebug.java revision 5bcdff45bf4ada77ae7c95f520b795876adef75c
19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.view;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.util.Log;
20c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.util.DisplayMetrics;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.content.res.Resources;
22105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Projectimport android.content.Context;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.graphics.Bitmap;
24c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.graphics.Canvas;
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Environment;
26c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport android.os.Debug;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.File;
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.BufferedWriter;
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileWriter;
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.FileOutputStream;
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.DataOutputStream;
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStreamWriter;
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.BufferedOutputStream;
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.OutputStream;
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.List;
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.LinkedList;
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.ArrayList;
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.HashMap;
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.CountDownLatch;
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.util.concurrent.TimeUnit;
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.Target;
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.ElementType;
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.Retention;
469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.annotation.RetentionPolicy;
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.Field;
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.Method;
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.lang.reflect.InvocationTargetException;
50c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Projectimport java.lang.reflect.AccessibleObject;
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Various debugging/tracing tools related to {@link View} and the view hierarchy.
549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpublic class ViewDebug {
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Log tag used to log errors related to the consistency of the view hierarchy.
5813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
5913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
6013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
6113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final String CONSISTENCY_LOG_TAG = "ViewConsistency";
6213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
6313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
6413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Flag indicating the consistency check should check layout-related properties.
6513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
6613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
6713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
6813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final int CONSISTENCY_LAYOUT = 0x1;
6913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
7013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
7113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Flag indicating the consistency check should check drawing-related properties.
7213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
7313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
7413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
7513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static final int CONSISTENCY_DRAWING = 0x2;
7613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
7713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables or disables view hierarchy tracing. Any invoker of
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * check that this value is set to true as not to affect performance.
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final boolean TRACE_HIERARCHY = false;
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Enables or disables view recycler tracing. Any invoker of
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])} should first
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * check that this value is set to true as not to affect performance.
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static final boolean TRACE_RECYCLER = false;
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The system property of dynamic switch for capturing view information
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * when it is set, we dump interested fields and methods for the view on focus
949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String SYSTEM_PROPERTY_CAPTURE_VIEW = "debug.captureview";
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * The system property of dynamic switch for capturing event information
999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * when it is set, we log key events, touch/motion and trackball events
1009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent";
102c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
10413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Profiles drawing times in the events log.
10513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
10613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
10713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
10813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
10913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean profileDrawing = false;
11013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
11113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
11213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Profiles layout times in the events log.
11313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
11413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
11513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
11613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
11713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean profileLayout = false;
11813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
11913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
12013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * Profiles real fps (times between draws) and displays the result.
12113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *
12213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
12313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
12413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
12513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean showFps = false;
12613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
12713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
12813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * <p>Enables or disables views consistency check. Even when this property is enabled,
12913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * view consistency checks happen only if {@link android.util.Config#DEBUG} is set
13013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * to true. The value of this property can be configured externally in one of the
13113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * following files:</p>
13213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * <ul>
13313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/system/debug.prop</li>
13413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/debug.prop</li>
13513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     *  <li>/data/debug.prop</li>
13613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * </ul>
13713922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     * @hide
13813922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy     */
13913922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    @Debug.DebugProperty
14013922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    public static boolean consistencyCheckEnabled = false;
14113922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
14213922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    static {
14313922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy        Debug.setFieldsOn(ViewDebug.class, true);
14413922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    }
14513922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy
14613922e03037d71a538f24ddf61c0b61bb4eb5af0Romain Guy    /**
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This annotation can be used to mark fields and methods to be dumped by
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the view server. Only non-void methods with no arguments can be annotated
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * by this annotation.
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.FIELD, ElementType.METHOD })
1529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface ExportedProperty {
1549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When resolveId is true, and if the annotated field/method return value
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * is an int, the value is converted to an Android's resource name.
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the property's value must be transformed into an Android
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *         resource name, false otherwise
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean resolveId() default false;
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * A mapping can be defined to map int values to specific strings. For
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * instance, View.getVisibility() returns 0, 4 or 8. However, these values
1669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * actually mean VISIBLE, INVISIBLE and GONE. A mapping can be used to see
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * these human readable values:
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <pre>
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @ViewDebug.ExportedProperty(mapping = {
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 0, to = "VISIBLE"),
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *     @ViewDebug.IntToString(from = 8, to = "GONE")
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * })
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * public int getVisibility() { ...
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * <pre>
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An array of int to String mappings
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see android.view.ViewDebug.IntToString
1819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        IntToString[] mapping() default { };
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
185c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * A mapping can be defined to map array indices to specific strings.
186c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * A mapping can be used to see human readable values for the indices
187c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * of an array:
188c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
189c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * <pre>
190809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @ViewDebug.ExportedProperty(indexMapping = {
191c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 0, to = "INVALID"),
192c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 1, to = "FIRST"),
193c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *     @ViewDebug.IntToString(from = 2, to = "SECOND")
194c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * })
195c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * private int[] mElements;
196c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * <pre>
197c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
198c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @return An array of int to String mappings
199c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         *
200c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @see android.view.ViewDebug.IntToString
201c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         * @see #mapping()
202c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project         */
203c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        IntToString[] indexMapping() default { };
204c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
205c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        /**
206809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * A flags mapping can be defined to map flags encoded in an integer to
207809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * specific strings. A mapping can be used to see human readable values
208809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * for the flags of an integer:
209809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
210809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <pre>
211809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @ViewDebug.ExportedProperty(flagMapping = {
212809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *     @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = ENABLED, name = "ENABLED"),
213809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *     @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = DISABLED, name = "DISABLED"),
214809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * })
215809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * private int mFlags;
216809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <pre>
217809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
218809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * A specified String is output when the following is true:
219809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
220809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An array of int to String mappings
221809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
222809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        FlagToString[] flagMapping() default { };
223809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
224809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
2259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When deep export is turned on, this property is not dumped. Instead, the
2269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * properties contained in this property are dumped. Each child property
2279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * is prefixed with the name of this property.
2289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if the properties of this property should be dumped
2309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see #prefix()
2329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean deepExport() default false;
2349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The prefix to use on child properties when deep export is enabled
2379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return a prefix as a String
2399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @see #deepExport()
2419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String prefix() default "";
2439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
2469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines a mapping from an int value to a String. Such a mapping can be used
2479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * in a @ExportedProperty to provide more meaningful values to the end user.
2489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
2499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see android.view.ViewDebug.ExportedProperty
2509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
2519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.TYPE })
2529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
2539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface IntToString {
2549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The original int value to map to a String.
2569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An arbitrary int value.
2589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int from();
2609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
2619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
2629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * The String to use in place of the original int value.
2639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         *
2649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return An arbitrary non-null String.
2659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
2669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        String to();
2679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
268809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
269809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    /**
270809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * Defines a mapping from an flag to a String. Such a mapping can be used
271809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * in a @ExportedProperty to provide more meaningful values to the end user.
272809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     *
273809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     * @see android.view.ViewDebug.ExportedProperty
274809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy     */
275809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    @Target({ ElementType.TYPE })
276809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    @Retention(RetentionPolicy.RUNTIME)
277809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    public @interface FlagToString {
278809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
279809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The mask to apply to the original value.
280809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
281809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary int value.
282809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
283809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        int mask();
284809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
285809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
286809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The value to compare to the result of:
287809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * <code>original value &amp; {@link #mask()}</code>.
288809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
289809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary value.
290809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
291809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        int equals();
292809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
293809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
294809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * The String to use in place of the original int value.
295809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         *
296809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * @return An arbitrary non-null String.
297809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
298809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        String name();
299809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
300809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        /**
301809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * Indicates whether to output the flag when the test is true,
302809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         * or false. Defaults to true.
303809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy         */
304809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        boolean outputIf() default true;
305809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    }
306809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
3079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This annotation can be used to mark fields and methods to be dumped when
3099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the view is captured. Methods with this annotation must have no arguments
310f8a7ceaef2e7d5cd530c9426bde91b6fa9a40b75Andy Stadler     * and must return a valid type of data.
3119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Target({ ElementType.FIELD, ElementType.METHOD })
3139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    @Retention(RetentionPolicy.RUNTIME)
3149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public @interface CapturedViewProperty {
3159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        /**
3169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * When retrieveReturn is true, we need to retrieve second level methods
3179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod()
3189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * we will set retrieveReturn = true on the annotation of
3199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * myView.getFirstLevelMethod()
3209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         * @return true if we need the second level methods
3219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project         */
3229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        boolean retrieveReturn() default false;
3239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
3269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
3279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    // Maximum delay in ms after which we stop trying to capture a View's drawing
3299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int CAPTURE_TIMEOUT = 4000;
3309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
3329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_DUMP = "DUMP";
3339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
3349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
335c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static final String REMOTE_PROFILE = "PROFILE";
3369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Field[]> sFieldsForClasses;
3389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static HashMap<Class<?>, Method[]> sMethodsForClasses;
339c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
340c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
3419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines the type of hierarhcy trace to output to the hierarchy traces file.
3439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum HierarchyTraceType {
3459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE,
3469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE_CHILD,
3479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        INVALIDATE_CHILD_IN_PARENT,
3489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        REQUEST_LAYOUT,
3499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ON_LAYOUT,
3509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        ON_MEASURE,
3519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        DRAW,
3529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BUILD_CACHE
3539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static BufferedWriter sHierarchyTraces;
3569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static ViewRoot sHierarhcyRoot;
3579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sHierarchyTracePrefix;
3589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Defines the type of recycler trace to output to the recycler traces file.
3619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public enum RecyclerTraceType {
3639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        NEW_VIEW,
3649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BIND_VIEW,
3659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RECYCLE_FROM_ACTIVE_HEAP,
3669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RECYCLE_FROM_SCRAP_HEAP,
3679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_TO_ACTIVE_HEAP,
3689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_TO_SCRAP_HEAP,
3699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
3709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static class RecyclerTrace {
3739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int view;
3749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public RecyclerTraceType type;
3759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int position;
3769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        public int indexOnScreen;
3779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static View sRecyclerOwnerView;
3809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static List<View> sRecyclerViews;
3819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static List<RecyclerTrace> sRecyclerTraces;
3829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String sRecyclerTracePrefix;
3839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of instanciated Views.
3869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The number of Views instanciated in the current process.
3889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
3909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
3919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static long getViewInstanceCount() {
3929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return View.sInstanceCount;
3939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
3949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
3959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
3969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Returns the number of instanciated ViewRoots.
3979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
3989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return The number of ViewRoots instanciated in the current process.
3999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @hide
4019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static long getViewRootInstanceCount() {
4039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return ViewRoot.getInstanceCount();
4049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Outputs a trace to the currently opened recycler traces. The trace records the type of
4089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * recycler action performed on the supplied view as well as a number of parameters.
4099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view to trace
4119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param type the type of the trace
4129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param parameters parameters depending on the type of the trace
4139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void trace(View view, RecyclerTraceType type, int... parameters) {
4159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
4169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!sRecyclerViews.contains(view)) {
4209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sRecyclerViews.add(view);
4219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int index = sRecyclerViews.indexOf(view);
4249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        RecyclerTrace trace = new RecyclerTrace();
4269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.view = index;
4279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.type = type;
4289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.position = parameters[0];
4299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        trace.indexOnScreen = parameters[1];
4309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces.add(trace);
4329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Starts tracing the view recycler of the specified view. The trace is identified by a prefix,
4369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used to build the traces files names: <code>/EXTERNAL/view-recycler/PREFIX.traces</code> and
4379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>.
4389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one view recycler can be traced at the same time. After calling this method, any
4409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * other invocation will result in a <code>IllegalStateException</code> unless
4419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #stopRecyclerTracing()} is invoked before.
4429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Traces files are created only after {@link #stopRecyclerTracing()} is invoked.
4449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_RECYCLER is false.
4469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param prefix the traces files name prefix
4489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view whose recycler must be traced
4499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #stopRecyclerTracing()
4519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
4529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void startRecyclerTracing(String prefix, View view) {
4549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
4559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_RECYCLER) {
4569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView != null) {
4609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call stopRecyclerTracing() before running" +
4619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " a new trace!");
4629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTracePrefix = prefix;
4659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerOwnerView = view;
4669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews = new ArrayList<View>();
4679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces = new LinkedList<RecyclerTrace>();
4689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
4699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
4719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stops the current view recycer tracing.
4729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.traces</code>
4749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all the traces (or method calls) relative to the specified view's recycler.
4759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>
4779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all of the views used by the recycler of the view supplied to
4789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #startRecyclerTracing(String, View)}.
4799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_RECYCLER is false.
4819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
4829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #startRecyclerTracing(String, View)
4839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
4849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
4859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void stopRecyclerTracing() {
4869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
4879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_RECYCLER) {
4889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
4899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
4929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call startRecyclerTracing() before" +
4939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " stopRecyclerTracing()!");
4949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
4959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
4969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
497c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
4989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump.mkdirs();
4999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler");
5019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final BufferedWriter out = new BufferedWriter(new FileWriter(recyclerDump), 8 * 1024);
5039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (View view : sRecyclerViews) {
5059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final String name = view.getClass().getName();
5069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(name);
5079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.newLine();
5089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.close();
5119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
5129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump recycler content");
5139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
5179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
5189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final FileOutputStream file = new FileOutputStream(recyclerDump);
5209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final DataOutputStream out = new DataOutputStream(file);
5219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (RecyclerTrace trace : sRecyclerTraces) {
5239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.view);
5249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.type.ordinal());
5259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.position);
5269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.writeInt(trace.indexOnScreen);
5279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.flush();
5289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
5299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.close();
5319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
5329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump recycler traces");
5339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews.clear();
5379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerViews = null;
5389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces.clear();
5409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerTraces = null;
5419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sRecyclerOwnerView = null;
5439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Outputs a trace to the currently opened traces file. The trace contains the class name
5479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * and instance's hashcode of the specified view as well as the supplied trace type.
5489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view to trace
5509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param type the type of the trace
5519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void trace(View view, HierarchyTraceType type) {
5539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarchyTraces == null) {
5549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
5589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(type.name());
5599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(' ');
5609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(view.getClass().getName());
5619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write('@');
5629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.write(Integer.toHexString(view.hashCode()));
5639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.newLine();
5649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
5659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping trace of type " + type + " for view " + view);
5669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
5689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
5709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Starts tracing the view hierarchy of the specified view. The trace is identified by a prefix,
5719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * used to build the traces files names: <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code> and
5729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>.
5739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Only one view hierarchy can be traced at the same time. After calling this method, any
5759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * other invocation will result in a <code>IllegalStateException</code> unless
5769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #stopHierarchyTracing()} is invoked before.
5779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>
5799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing all the traces (or method calls) relative to the specified view's hierarchy.
5809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_HIERARCHY is false.
5829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param prefix the traces files name prefix
5849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view the view whose hierarchy must be traced
5859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
5869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #stopHierarchyTracing()
5879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
5889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
5899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void startHierarchyTracing(String prefix, View view) {
5909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        //noinspection PointlessBooleanExpression,ConstantConditions
5919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!TRACE_HIERARCHY) {
5929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
5939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
5959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sHierarhcyRoot != null) {
5969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
5979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " a new trace!");
5989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
5999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
601c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
6029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump.mkdirs();
6039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump = new File(hierarchyDump, prefix + ".traces");
6059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarchyTracePrefix = prefix;
6069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
6089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
6099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
6109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump view hierarchy");
6119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarhcyRoot = (ViewRoot) view.getRootView().getParent();
6159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
6189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Stops the current view hierarchy tracing. This method closes the file
6199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>.
6209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>
6229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * containing the view hierarchy of the view supplied to
6239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * {@link #startHierarchyTracing(String, View)}.
6249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method will return immediately if TRACE_HIERARCHY is false.
6269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
6279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @see #startHierarchyTracing(String, View)
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 stopHierarchyTracing() {
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 || sHierarchyTraces == null) {
6379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new IllegalStateException("You must call startHierarchyTracing() before" +
6389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                " stopHierarchyTracing()!");
6399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
6429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sHierarchyTraces.close();
6439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
6449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not write view traces");
6459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarchyTraces = null;
6479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
649c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        //noinspection ResultOfMethodCallIgnored
6509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump.mkdirs();
6519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree");
6529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedWriter out;
6549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
6559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
6569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
6579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.e("View", "Could not dump view hierarchy");
6589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
6599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        View view = sHierarhcyRoot.getView();
6629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view instanceof ViewGroup) {
6639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            ViewGroup group = (ViewGroup) view;
6649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dumpViewHierarchy(group, out, 0);
6659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
6669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
6679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException e) {
6689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Log.e("View", "Could not dump view hierarchy");
6699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sHierarhcyRoot = null;
6739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    static void dispatchCommand(View view, String command, String parameters,
6769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            OutputStream clientStream) throws IOException {
6779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        // Paranoid but safe...
6799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        view = view.getRootView();
6809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
6819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
6829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            dump(view, clientStream);
6839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
6849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final String[] params = parameters.split(" ");
6859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
6869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                capture(view, clientStream, params[0]);
6879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (REMOTE_COMMAND_INVALIDATE.equalsIgnoreCase(command)) {
6889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                invalidate(view, params[0]);
6899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) {
6909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                requestLayout(view, params[0]);
691c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } else if (REMOTE_PROFILE.equalsIgnoreCase(command)) {
692c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                profile(view, clientStream, params[0]);
6939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
6949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
6959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
6969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
697c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static View findView(View root, String parameter) {
698c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        // Look by type/hashcode
699c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (parameter.indexOf('@') != -1) {
700c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final String[] ids = parameter.split("@");
701c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final String className = ids[0];
702c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int hashCode = Integer.parseInt(ids[1], 16);
7039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
704c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            View view = root.getRootView();
705c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (view instanceof ViewGroup) {
706c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                return findView((ViewGroup) view, className, hashCode);
707c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
708c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
709c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            // Look by id
710c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int id = root.getResources().getIdentifier(parameter, null, null);
711c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            return root.getRootView().findViewById(id);
7129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
7159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void invalidate(View root, String parameter) {
718c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
7199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
7209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            view.postInvalidate();
7219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
7249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void requestLayout(View root, String parameter) {
725c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
7269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (view != null) {
7279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            root.post(new Runnable() {
7289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                public void run() {
7299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    view.requestLayout();
7309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
7319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            });
7329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
7339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
7349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
735c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void profile(View root, OutputStream clientStream, String parameter)
7369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throws IOException {
7379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
738c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View view = findView(root, parameter);
739c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        BufferedWriter out = null;
740c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        try {
741c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
742c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
743c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (view != null) {
744c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                final long durationMeasure = profileViewOperation(view, new ViewOperation<Void>() {
745c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public Void[] pre() {
746c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        forceLayout(view);
747c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        return null;
748c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
749c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
750c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    private void forceLayout(View view) {
751c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        view.forceLayout();
752c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        if (view instanceof ViewGroup) {
753c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                            ViewGroup group = (ViewGroup) view;
754c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                            final int count = group.getChildCount();
755c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                            for (int i = 0; i < count; i++) {
756c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                                forceLayout(group.getChildAt(i));
757c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                            }
758c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        }
759c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
760c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
761c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void run(Void... data) {
762c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
763c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
764c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
765c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void post(Void... data) {
766c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
767c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                });
768c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
769c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                final long durationLayout = profileViewOperation(view, new ViewOperation<Void>() {
770c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public Void[] pre() {
771c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        return null;
772c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
773c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
774c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void run(Void... data) {
775c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
776c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
777c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
778c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void post(Void... data) {
779c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
780c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                });
781c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
782c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                final long durationDraw = profileViewOperation(view, new ViewOperation<Object>() {
783c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public Object[] pre() {
784c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        final DisplayMetrics metrics = view.getResources().getDisplayMetrics();
785c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        final Bitmap bitmap = Bitmap.createBitmap(metrics.widthPixels,
786ba87e3e6c985e7175152993b5efcc7dd2f0e1c93The Android Open Source Project                                metrics.heightPixels, Bitmap.Config.RGB_565);
787c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        final Canvas canvas = new Canvas(bitmap);
788c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        return new Object[] { bitmap, canvas };
789c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
790c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
791c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void run(Object... data) {
792c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        view.draw((Canvas) data[1]);
793c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
794c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
795c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    public void post(Object... data) {
796c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        ((Bitmap) data[0]).recycle();
797c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
798c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                });
799c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
800c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(String.valueOf(durationMeasure));
801c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(' ');
802c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(String.valueOf(durationLayout));
803c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(' ');
804c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write(String.valueOf(durationDraw));
805c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.newLine();
806c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } else {
807c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.write("-1 -1 -1");
808c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.newLine();
809c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
810c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (Exception e) {
811c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            android.util.Log.w("View", "Problem profiling the view:", e);
812c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } finally {
813c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (out != null) {
814c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                out.close();
815c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
816c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
817c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
818c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
819c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    interface ViewOperation<T> {
820c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        T[] pre();
821c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        void run(T... data);
822c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        void post(T... data);
823c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
824c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
825c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) {
8269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final CountDownLatch latch = new CountDownLatch(1);
827c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final long[] duration = new long[1];
828c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
829c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        view.post(new Runnable() {
830c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            public void run() {
831c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                try {
832c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    T[] data = operation.pre();
833c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    long start = Debug.threadCpuTimeNanos();
834c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    operation.run(data);
835c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    duration[0] = Debug.threadCpuTimeNanos() - start;
836c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    operation.post(data);
837c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } finally {
838c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    latch.countDown();
839c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
840c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
841c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        });
842c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
843c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        try {
844c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
845c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (InterruptedException e) {
846c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            Log.w("View", "Could not complete the profiling of the view " + view);
847c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            Thread.currentThread().interrupt();
848c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            return -1;
849c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
850c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
851c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        return duration[0];
852c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
853c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
854c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void capture(View root, final OutputStream clientStream, String parameter)
855c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            throws IOException {
856c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
857c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final View captureView = findView(root, parameter);
8589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (captureView != null) {
860c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final CountDownLatch latch = new CountDownLatch(1);
8619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Bitmap[] cache = new Bitmap[1];
8629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            root.post(new Runnable() {
8649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                public void run() {
8659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
866958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                        cache[0] = captureView.createSnapshot(
867958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                                Bitmap.Config.ARGB_8888, 0);
868958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                    } catch (OutOfMemoryError e) {
869958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                        try {
870958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                            cache[0] = captureView.createSnapshot(
871958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                                    Bitmap.Config.ARGB_4444, 0);
872958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                        } catch (OutOfMemoryError e2) {
873958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                            Log.w("View", "Out of memory for bitmap");
8749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
8759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } finally {
8769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        latch.countDown();
8779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
8789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
8799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            });
8809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
8829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
8839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
8849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (cache[0] != null) {
8859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    BufferedOutputStream out = null;
8869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    try {
8879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out = new BufferedOutputStream(clientStream, 32 * 1024);
8889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        cache[0].compress(Bitmap.CompressFormat.PNG, 100, out);
8899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        out.flush();
8909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } finally {
8919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (out != null) {
8929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            out.close();
8939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
894958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                        cache[0].recycle();
8959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
896958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                } else {
897958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                    Log.w("View", "Failed to create capture bitmap!");
898958b9adc086f126dcd757d29f0d7f443ae9064b2Dianne Hackborn                    clientStream.close();
8999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (InterruptedException e) {
901c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                Log.w("View", "Could not complete the capture of the view " + captureView);
902c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                Thread.currentThread().interrupt();
9039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dump(View root, OutputStream clientStream) throws IOException {
9089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        BufferedWriter out = null;
9099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
9109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
9119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            View view = root.getRootView();
9129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
9139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                ViewGroup group = (ViewGroup) view;
914105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewHierarchyWithProperties(group.getContext(), group, out, 0);
9159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write("DONE.");
9179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
918c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } catch (Exception e) {
919c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            android.util.Log.w("View", "Problem dumping the view:", e);
9209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } finally {
9219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (out != null) {
9229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.close();
9239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static View findView(ViewGroup group, String className, int hashCode) {
9289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (isRequestedView(group, className, hashCode)) {
9299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return group;
9309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
9339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
9349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
9359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
9369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final View found = findView((ViewGroup) view, className, hashCode);
9379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (found != null) {
9389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return found;
9399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
9409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else if (isRequestedView(view, className, hashCode)) {
9419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                return view;
9429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return null;
9469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean isRequestedView(View view, String className, int hashCode) {
9499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
9509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
952105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewHierarchyWithProperties(Context context, ViewGroup group,
9539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            BufferedWriter out, int level) {
954105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        if (!dumpViewWithProperties(context, group, out, level)) {
9559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
9569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
9599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
9609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
9619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
962105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewHierarchyWithProperties(context, (ViewGroup) view, out, level + 1);
9639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
964105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                dumpViewWithProperties(context, view, out, level + 1);
9659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
969105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static boolean dumpViewWithProperties(Context context, View view,
970105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out, int level) {
971105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
9729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
9739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < level; i++) {
9749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(' ');
9759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
9769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(view.getClass().getName());
9779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write('@');
9789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(Integer.toHexString(view.hashCode()));
9799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(' ');
980105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            dumpViewProperties(context, view, out);
9819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
9829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
9839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping hierarchy tree");
9849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
9859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
9869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
9879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
9889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
9899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Field[] getExportedPropertyFields(Class<?> klass) {
9909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sFieldsForClasses == null) {
9919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            sFieldsForClasses = new HashMap<Class<?>, Field[]>();
9929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
993c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (sAnnotations == null) {
994c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
995c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
996c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
9979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
998c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
9999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Field[] fields = map.get(klass);
10019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fields != null) {
10029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fields;
10039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Field> foundFields = new ArrayList<Field>();
10069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = klass.getDeclaredFields();
10079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
10099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
10109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
10119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (field.isAnnotationPresent(ExportedProperty.class)) {
10129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                field.setAccessible(true);
10139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundFields.add(field);
1014c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                annotations.put(field, field.getAnnotation(ExportedProperty.class));
10159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = foundFields.toArray(new Field[foundFields.size()]);
10199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, fields);
10209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fields;
10229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Method[] getExportedPropertyMethods(Class<?> klass) {
10259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (sMethodsForClasses == null) {
1026c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
1027c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1028c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (sAnnotations == null) {
1029c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
10309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1031c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
10329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
1033c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final HashMap<AccessibleObject, ExportedProperty> annotations = sAnnotations;
10349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Method[] methods = map.get(klass);
10369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (methods != null) {
10379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return methods;
10389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Method> foundMethods = new ArrayList<Method>();
10419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = klass.getDeclaredMethods();
10429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
10449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
10459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
10469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (method.getParameterTypes().length == 0 &&
1047c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    method.isAnnotationPresent(ExportedProperty.class) &&
1048c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    method.getReturnType() != Void.class) {
10499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                method.setAccessible(true);
10509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundMethods.add(method);
1051c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                annotations.put(method, method.getAnnotation(ExportedProperty.class));
10529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
10539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
10549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = foundMethods.toArray(new Method[foundMethods.size()]);
10569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, methods);
10579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return methods;
10599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1061105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewProperties(Context context, Object view,
1062105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out) throws IOException {
1063105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
1064105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        dumpViewProperties(context, view, out, "");
10659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1067105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void dumpViewProperties(Context context, Object view,
1068105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            BufferedWriter out, String prefix) throws IOException {
1069105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
10709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> klass = view.getClass();
10719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        do {
1073105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            exportFields(context, view, out, klass, prefix);
1074105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            exportMethods(context, view, out, klass, prefix);
10759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            klass = klass.getSuperclass();
10769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } while (klass != Object.class);
10779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
10789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1079105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportMethods(Context context, Object view, BufferedWriter out,
1080105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            Class<?> klass, String prefix) throws IOException {
10819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Method[] methods = getExportedPropertyMethods(klass);
10839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
10859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
10869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
10879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection EmptyCatchBlock
10889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
10899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // TODO: This should happen on the UI thread
10909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object methodValue = method.invoke(view, (Object[]) null);
10919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> returnType = method.getReturnType();
10929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
10939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (returnType == int.class) {
1094c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(method);
1095105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    if (property.resolveId() && context != null) {
10969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final int id = (Integer) methodValue;
1097105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        methodValue = resolveId(context, id);
10989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1099809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        final FlagToString[] flagsMapping = property.flagMapping();
1100809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        if (flagsMapping.length > 0) {
1101809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            final int intValue = (Integer) methodValue;
1102809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            final String valuePrefix = prefix + method.getName() + '_';
1103809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
1104809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        }
1105809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
11069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final IntToString[] mapping = property.mapping();
11079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mapping.length > 0) {
11089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            final int intValue = (Integer) methodValue;
11099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            boolean mapped = false;
11109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            int mappingCount = mapping.length;
11119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            for (int j = 0; j < mappingCount; j++) {
11129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                final IntToString mapper = mapping[j];
11139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                if (mapper.from() == intValue) {
11149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    methodValue = mapper.to();
11159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    mapped = true;
11169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    break;
11179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                }
11189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
11199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (!mapped) {
11219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                methodValue = intValue;
11229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
11239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
11249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1125c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } else if (returnType == int[].class) {
1126c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(method);
1127c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final int[] array = (int[]) methodValue;
1128c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String valuePrefix = prefix + method.getName() + '_';
1129c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String suffix = "()";
1130c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1131105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
11329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!returnType.isPrimitive()) {
1133c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(method);
11349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.deepExport()) {
1135105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        dumpViewProperties(context, methodValue, out, prefix + property.prefix());
11369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
11379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
11389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
11399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1140c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                writeEntry(out, prefix, method.getName(), "()", methodValue);
11419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
11429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (InvocationTargetException e) {
11439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
11449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
11459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
11469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1147105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportFields(Context context, Object view, BufferedWriter out,
1148105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project            Class<?> klass, String prefix) throws IOException {
1149105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project
11509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Field[] fields = getExportedPropertyFields(klass);
11519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
11539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
11549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
11559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            //noinspection EmptyCatchBlock
11579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
11589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object fieldValue = null;
11599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> type = field.getType();
11609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (type == int.class) {
1162c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(field);
1163105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    if (property.resolveId() && context != null) {
11649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final int id = field.getInt(view);
1165105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        fieldValue = resolveId(context, id);
11669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
1167809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        final FlagToString[] flagsMapping = property.flagMapping();
1168809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        if (flagsMapping.length > 0) {
1169809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            final int intValue = field.getInt(view);
1170809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            final String valuePrefix = prefix + field.getName() + '_';
1171809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                            exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
1172809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                        }
1173809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
11749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final IntToString[] mapping = property.mapping();
11759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        if (mapping.length > 0) {
11769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            final int intValue = field.getInt(view);
11779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            int mappingCount = mapping.length;
11789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            for (int j = 0; j < mappingCount; j++) {
11799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                final IntToString mapped = mapping[j];
11809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                if (mapped.from() == intValue) {
11819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    fieldValue = mapped.to();
11829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                    break;
11839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                }
11849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
11859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
11869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            if (fieldValue == null) {
11879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                                fieldValue = intValue;
11889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                            }
11899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        }
11909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
1191c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                } else if (type == int[].class) {
1192c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(field);
1193c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final int[] array = (int[]) field.get(view);
1194c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String valuePrefix = prefix + field.getName() + '_';
1195c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final String suffix = "";
1196c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1197105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                    exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
1198c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1199c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    // We exit here!
1200c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    return;
12019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else if (!type.isPrimitive()) {
1202c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final ExportedProperty property = sAnnotations.get(field);
12039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (property.deepExport()) {
1204105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                        dumpViewProperties(context, field.get(view), out,
1205105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                                prefix + property.prefix());
12069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        continue;
12079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
12089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
12109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fieldValue == null) {
12119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    fieldValue = field.get(view);
12129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
12139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1214c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                writeEntry(out, prefix, field.getName(), "", fieldValue);
12159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
12169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
12179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
12189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
12199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1220c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void writeEntry(BufferedWriter out, String prefix, String name,
1221c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String suffix, Object value) throws IOException {
1222c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1223c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(prefix);
1224c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(name);
1225c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(suffix);
1226c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write("=");
1227c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        writeValue(out, value);
1228c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        out.write(' ');
1229c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1230c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1231809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    private static void exportUnrolledFlags(BufferedWriter out, FlagToString[] mapping,
1232809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            int intValue, String prefix) throws IOException {
1233809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
1234809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        final int count = mapping.length;
1235809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        for (int j = 0; j < count; j++) {
1236809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            final FlagToString flagMapping = mapping[j];
1237809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            final boolean ifTrue = flagMapping.outputIf();
12385bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy            final int maskResult = intValue & flagMapping.mask();
12395bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy            final boolean test = maskResult == flagMapping.equals();
1240809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            if ((test && ifTrue) || (!test && !ifTrue)) {
1241809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                final String name = flagMapping.name();
12425bcdff45bf4ada77ae7c95f520b795876adef75cRomain Guy                final String value = "0x" + Integer.toHexString(maskResult);
1243809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy                writeEntry(out, prefix, name, "", value);
1244809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy            }
1245809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy        }
1246809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy    }
1247809a7f6080312f3e12f1a3a30eacf0e0c7627305Romain Guy
1248105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static void exportUnrolledArray(Context context, BufferedWriter out,
1249c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            ExportedProperty property, int[] array, String prefix, String suffix)
1250c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            throws IOException {
1251c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1252c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final IntToString[] indexMapping = property.indexMapping();
1253c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean hasIndexMapping = indexMapping.length > 0;
1254c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1255c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final IntToString[] mapping = property.mapping();
1256c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final boolean hasMapping = mapping.length > 0;
1257c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1258105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        final boolean resolveId = property.resolveId() && context != null;
1259c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        final int valuesCount = array.length;
1260c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1261c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        for (int j = 0; j < valuesCount; j++) {
1262c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String name;
1263c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String value;
1264c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1265c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            final int intValue = array[j];
1266c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1267c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            name = String.valueOf(j);
1268c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (hasIndexMapping) {
1269c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                int mappingCount = indexMapping.length;
1270c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                for (int k = 0; k < mappingCount; k++) {
1271c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final IntToString mapped = indexMapping[k];
1272c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    if (mapped.from() == j) {
1273c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        name = mapped.to();
1274c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        break;
1275c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
1276c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1277c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1278c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1279c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            value = String.valueOf(intValue);
1280c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (hasMapping) {
1281c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                int mappingCount = mapping.length;
1282c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                for (int k = 0; k < mappingCount; k++) {
1283c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    final IntToString mapped = mapping[k];
1284c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    if (mapped.from() == intValue) {
1285c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        value = mapped.to();
1286c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        break;
1287c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                    }
1288c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                }
1289c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1290c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1291c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            if (resolveId) {
1292105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project                value = (String) resolveId(context, intValue);
1293c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1294c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1295c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            writeEntry(out, prefix, name, suffix, value);
1296c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1297c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1298c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1299105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project    private static Object resolveId(Context context, int id) {
1300c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        Object fieldValue;
1301105925376f8d0f6b318c9938c7b83ef7fef094daThe Android Open Source Project        final Resources resources = context.getResources();
1302c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (id >= 0) {
1303c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            try {
1304c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                fieldValue = resources.getResourceTypeName(id) + '/' +
1305c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                        resources.getResourceEntryName(id);
1306c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            } catch (Resources.NotFoundException e) {
1307c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project                fieldValue = "id/0x" + Integer.toHexString(id);
1308c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            }
1309c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
1310c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            fieldValue = "NO_ID";
1311c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1312c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        return fieldValue;
1313c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1314c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
1315c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    private static void writeValue(BufferedWriter out, Object value) throws IOException {
1316c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        if (value != null) {
1317c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            String output = value.toString().replace("\n", "\\n");
1318c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(String.valueOf(output.length()));
1319c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(",");
1320c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write(output);
1321c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        } else {
1322c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project            out.write("4,null");
1323c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project        }
1324c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project    }
1325c39a6e0c51e182338deb8b63d07933b585134929The Android Open Source Project
13269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static void dumpViewHierarchy(ViewGroup group, BufferedWriter out, int level) {
13279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (!dumpView(group, out, level)) {
13289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return;
13299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final int count = group.getChildCount();
13329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
13339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final View view = group.getChildAt(i);
13349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (view instanceof ViewGroup) {
13359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpViewHierarchy((ViewGroup) view, out, level + 1);
13369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } else {
13379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                dumpView(view, out, level + 1);
13389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static boolean dumpView(Object view, BufferedWriter out, int level) {
13439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        try {
13449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            for (int i = 0; i < level; i++) {
13459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                out.write(' ');
13469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(view.getClass().getName());
13489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write('@');
13499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.write(Integer.toHexString(view.hashCode()));
13509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            out.newLine();
13519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } catch (IOException e) {
13529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            Log.w("View", "Error while dumping hierarchy tree");
13539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return false;
13549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
13569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
13599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mCapturedViewFieldsForClasses == null) {
13609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
13619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
13639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Field[] fields = map.get(klass);
13659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (fields != null) {
13669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return fields;
13679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Field> foundFields = new ArrayList<Field>();
13709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = klass.getFields();
13719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
13739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
13749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
13759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (field.isAnnotationPresent(CapturedViewProperty.class)) {
13769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                field.setAccessible(true);
13779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundFields.add(field);
13789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
13799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        fields = foundFields.toArray(new Field[foundFields.size()]);
13829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, fields);
13839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return fields;
13859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
13869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
13889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mCapturedViewMethodsForClasses == null) {
13899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
13909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
13929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Method[] methods = map.get(klass);
13949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (methods != null) {
13959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return methods;
13969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
13979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
13989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final ArrayList<Method> foundMethods = new ArrayList<Method>();
13999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = klass.getMethods();
14009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
14029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
14039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
14049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (method.getParameterTypes().length == 0 &&
14059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    method.isAnnotationPresent(CapturedViewProperty.class) &&
14069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    method.getReturnType() != Void.class) {
14079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                method.setAccessible(true);
14089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                foundMethods.add(method);
14099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        methods = foundMethods.toArray(new Method[foundMethods.size()]);
14139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        map.put(klass, methods);
14149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return methods;
14169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String capturedViewExportMethods(Object obj, Class<?> klass,
14199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            String prefix) {
14209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (obj == null) {
14229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "null";
14239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
14269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Method[] methods = capturedViewGetPropertyMethods(klass);
14279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = methods.length;
14299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
14309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Method method = methods[i];
14319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
14329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object methodValue = method.invoke(obj, (Object[]) null);
14339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                final Class<?> returnType = method.getReturnType();
14349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
14369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (property.retrieveReturn()) {
14379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    //we are interested in the second level data only
14389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
14399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
14409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(prefix);
14419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(method.getName());
14429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("()=");
14439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    if (methodValue != null) {
14459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        final String value = methodValue.toString().replace("\n", "\\n");
14469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sb.append(value);
14479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    } else {
14489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                        sb.append("null");
14499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    }
14509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("; ");
14519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
14529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              } catch (IllegalAccessException e) {
14539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //Exception IllegalAccess, it is OK here
14549066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //we simply ignore this method
14559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              } catch (InvocationTargetException e) {
14569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //Exception InvocationTarget, it is OK here
14579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                  //we simply ignore this method
14589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project              }
14599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
14619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
14649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (obj == null) {
14669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            return "null";
14679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder();
14709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        final Field[] fields = capturedViewGetPropertyFields(klass);
14719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        int count = fields.length;
14739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        for (int i = 0; i < count; i++) {
14749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            final Field field = fields[i];
14759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
14769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                Object fieldValue = field.get(obj);
14779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(prefix);
14799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(field.getName());
14809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append("=");
14819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (fieldValue != null) {
14839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    final String value = fieldValue.toString().replace("\n", "\\n");
14849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append(value);
14859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
14869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    sb.append("null");
14879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
14889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sb.append(' ');
14899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IllegalAccessException e) {
14909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                //Exception IllegalAccess, it is OK here
14919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                //we simply ignore this field
14929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
14939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
14949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return sb.toString();
14959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
14969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
14979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1498f8a7ceaef2e7d5cd530c9426bde91b6fa9a40b75Andy Stadler     * Dump view info for id based instrument test generation
14999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * (and possibly further data analysis). The results are dumped
15009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to the log.
15019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param tag for log
15029066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param view for dump
15039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
15049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    public static void dumpCapturedView(String tag, Object view) {
15059066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Class<?> klass = view.getClass();
15069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        StringBuilder sb = new StringBuilder(klass.getName() + ": ");
15079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sb.append(capturedViewExportFields(view, klass, ""));
15089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        sb.append(capturedViewExportMethods(view, klass, ""));
15099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Log.d(tag, sb.toString());
15109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
15119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
1512