blob: 92e5e964fd34f0a3d1f51be360eb12eda9744372 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.view;
18
The Android Open Source Project10592532009-03-18 17:39:46 -070019import android.content.Context;
Romain Guyf9284692011-07-13 18:46:21 -070020import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.graphics.Bitmap;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070022import android.graphics.Canvas;
Romain Guy223ff5c2010-03-02 17:07:47 -080023import android.graphics.Rect;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070024import android.os.Debug;
Kristian Monsen97e8f622013-04-26 12:51:19 -070025import android.os.Handler;
Romain Guy223ff5c2010-03-02 17:07:47 -080026import android.os.RemoteException;
Romain Guyf9284692011-07-13 18:46:21 -070027import android.util.DisplayMetrics;
28import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029
Romain Guyf9284692011-07-13 18:46:21 -070030import java.io.BufferedOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import java.io.BufferedWriter;
Romain Guyf9284692011-07-13 18:46:21 -070032import java.io.ByteArrayOutputStream;
33import java.io.DataOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import java.io.IOException;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import java.io.OutputStream;
Romain Guyf9284692011-07-13 18:46:21 -070036import java.io.OutputStreamWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import java.lang.annotation.ElementType;
38import java.lang.annotation.Retention;
39import java.lang.annotation.RetentionPolicy;
Romain Guyf9284692011-07-13 18:46:21 -070040import java.lang.annotation.Target;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -070041import java.lang.reflect.AccessibleObject;
Romain Guyf9284692011-07-13 18:46:21 -070042import java.lang.reflect.Field;
43import java.lang.reflect.InvocationTargetException;
44import java.lang.reflect.Method;
45import java.util.ArrayList;
46import java.util.HashMap;
Kristian Monsen97e8f622013-04-26 12:51:19 -070047import java.util.concurrent.Callable;
48import java.util.concurrent.CancellationException;
Romain Guyf9284692011-07-13 18:46:21 -070049import java.util.concurrent.CountDownLatch;
Kristian Monsen97e8f622013-04-26 12:51:19 -070050import java.util.concurrent.ExecutionException;
51import java.util.concurrent.FutureTask;
52import java.util.concurrent.TimeoutException;
Romain Guyf9284692011-07-13 18:46:21 -070053import java.util.concurrent.TimeUnit;
Siva Velusamyf9455fa2013-01-17 18:01:52 -080054import java.util.concurrent.atomic.AtomicReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055
56/**
57 * Various debugging/tracing tools related to {@link View} and the view hierarchy.
58 */
59public class ViewDebug {
60 /**
Romain Guy13b90732012-05-21 12:13:31 -070061 * @deprecated This flag is now unused
Romain Guy13922e02009-05-12 17:56:14 -070062 */
Romain Guy13b90732012-05-21 12:13:31 -070063 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064 public static final boolean TRACE_HIERARCHY = false;
65
66 /**
Romain Guy13b90732012-05-21 12:13:31 -070067 * @deprecated This flag is now unused
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 */
Romain Guy13b90732012-05-21 12:13:31 -070069 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 public static final boolean TRACE_RECYCLER = false;
Romain Guya1f3e4a2009-06-04 15:10:46 -070071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 /**
Christopher Tate2c095f32010-10-04 14:13:40 -070073 * Enables detailed logging of drag/drop operations.
74 * @hide
75 */
Christopher Tate994ef922011-01-12 20:06:07 -080076 public static final boolean DEBUG_DRAG = false;
Christopher Tate2c095f32010-10-04 14:13:40 -070077
78 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 * This annotation can be used to mark fields and methods to be dumped by
80 * the view server. Only non-void methods with no arguments can be annotated
81 * by this annotation.
82 */
83 @Target({ ElementType.FIELD, ElementType.METHOD })
84 @Retention(RetentionPolicy.RUNTIME)
85 public @interface ExportedProperty {
86 /**
87 * When resolveId is true, and if the annotated field/method return value
88 * is an int, the value is converted to an Android's resource name.
89 *
90 * @return true if the property's value must be transformed into an Android
91 * resource name, false otherwise
92 */
93 boolean resolveId() default false;
94
95 /**
96 * A mapping can be defined to map int values to specific strings. For
97 * instance, View.getVisibility() returns 0, 4 or 8. However, these values
98 * actually mean VISIBLE, INVISIBLE and GONE. A mapping can be used to see
99 * these human readable values:
100 *
101 * <pre>
102 * @ViewDebug.ExportedProperty(mapping = {
103 * @ViewDebug.IntToString(from = 0, to = "VISIBLE"),
104 * @ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
105 * @ViewDebug.IntToString(from = 8, to = "GONE")
106 * })
107 * public int getVisibility() { ...
108 * <pre>
109 *
110 * @return An array of int to String mappings
111 *
112 * @see android.view.ViewDebug.IntToString
113 */
114 IntToString[] mapping() default { };
115
116 /**
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700117 * A mapping can be defined to map array indices to specific strings.
118 * A mapping can be used to see human readable values for the indices
119 * of an array:
120 *
121 * <pre>
Romain Guy809a7f62009-05-14 15:44:42 -0700122 * @ViewDebug.ExportedProperty(indexMapping = {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700123 * @ViewDebug.IntToString(from = 0, to = "INVALID"),
124 * @ViewDebug.IntToString(from = 1, to = "FIRST"),
125 * @ViewDebug.IntToString(from = 2, to = "SECOND")
126 * })
127 * private int[] mElements;
128 * <pre>
129 *
130 * @return An array of int to String mappings
131 *
132 * @see android.view.ViewDebug.IntToString
133 * @see #mapping()
134 */
135 IntToString[] indexMapping() default { };
136
137 /**
Romain Guy809a7f62009-05-14 15:44:42 -0700138 * A flags mapping can be defined to map flags encoded in an integer to
139 * specific strings. A mapping can be used to see human readable values
140 * for the flags of an integer:
141 *
142 * <pre>
143 * @ViewDebug.ExportedProperty(flagMapping = {
144 * @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = ENABLED, name = "ENABLED"),
145 * @ViewDebug.FlagToString(mask = ENABLED_MASK, equals = DISABLED, name = "DISABLED"),
146 * })
147 * private int mFlags;
148 * <pre>
149 *
150 * A specified String is output when the following is true:
Romain Guya1f3e4a2009-06-04 15:10:46 -0700151 *
Romain Guy809a7f62009-05-14 15:44:42 -0700152 * @return An array of int to String mappings
153 */
154 FlagToString[] flagMapping() default { };
155
156 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 * When deep export is turned on, this property is not dumped. Instead, the
158 * properties contained in this property are dumped. Each child property
159 * is prefixed with the name of this property.
160 *
161 * @return true if the properties of this property should be dumped
162 *
Romain Guya1f3e4a2009-06-04 15:10:46 -0700163 * @see #prefix()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 */
165 boolean deepExport() default false;
166
167 /**
168 * The prefix to use on child properties when deep export is enabled
169 *
170 * @return a prefix as a String
171 *
172 * @see #deepExport()
173 */
174 String prefix() default "";
Konstantin Lopyrevbea95162010-08-10 17:02:18 -0700175
176 /**
177 * Specifies the category the property falls into, such as measurement,
178 * layout, drawing, etc.
179 *
180 * @return the category as String
181 */
182 String category() default "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800183 }
184
185 /**
186 * Defines a mapping from an int value to a String. Such a mapping can be used
Ken Wakasaf76a50c2012-03-09 19:56:35 +0900187 * in an @ExportedProperty to provide more meaningful values to the end user.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 *
189 * @see android.view.ViewDebug.ExportedProperty
190 */
191 @Target({ ElementType.TYPE })
192 @Retention(RetentionPolicy.RUNTIME)
193 public @interface IntToString {
194 /**
195 * The original int value to map to a String.
196 *
197 * @return An arbitrary int value.
198 */
199 int from();
200
201 /**
202 * The String to use in place of the original int value.
203 *
204 * @return An arbitrary non-null String.
205 */
206 String to();
207 }
Romain Guy809a7f62009-05-14 15:44:42 -0700208
209 /**
Ken Wakasaf76a50c2012-03-09 19:56:35 +0900210 * Defines a mapping from a flag to a String. Such a mapping can be used
211 * in an @ExportedProperty to provide more meaningful values to the end user.
Romain Guy809a7f62009-05-14 15:44:42 -0700212 *
213 * @see android.view.ViewDebug.ExportedProperty
214 */
215 @Target({ ElementType.TYPE })
216 @Retention(RetentionPolicy.RUNTIME)
217 public @interface FlagToString {
218 /**
219 * The mask to apply to the original value.
220 *
221 * @return An arbitrary int value.
222 */
223 int mask();
224
225 /**
226 * The value to compare to the result of:
227 * <code>original value &amp; {@link #mask()}</code>.
228 *
229 * @return An arbitrary value.
230 */
231 int equals();
232
233 /**
234 * The String to use in place of the original int value.
235 *
236 * @return An arbitrary non-null String.
237 */
238 String name();
239
240 /**
241 * Indicates whether to output the flag when the test is true,
242 * or false. Defaults to true.
243 */
244 boolean outputIf() default true;
245 }
246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 /**
248 * This annotation can be used to mark fields and methods to be dumped when
249 * the view is captured. Methods with this annotation must have no arguments
Andy Stadlerf8a7cea2009-04-10 16:24:47 -0700250 * and must return a valid type of data.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 */
252 @Target({ ElementType.FIELD, ElementType.METHOD })
253 @Retention(RetentionPolicy.RUNTIME)
254 public @interface CapturedViewProperty {
255 /**
Romain Guya1f3e4a2009-06-04 15:10:46 -0700256 * When retrieveReturn is true, we need to retrieve second level methods
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod()
Romain Guya1f3e4a2009-06-04 15:10:46 -0700258 * we will set retrieveReturn = true on the annotation of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 * myView.getFirstLevelMethod()
Romain Guya1f3e4a2009-06-04 15:10:46 -0700260 * @return true if we need the second level methods
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 */
Romain Guya1f3e4a2009-06-04 15:10:46 -0700262 boolean retrieveReturn() default false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 }
Romain Guya1f3e4a2009-06-04 15:10:46 -0700264
John Reck926cf562012-06-14 10:00:31 -0700265 /**
266 * Allows a View to inject custom children into HierarchyViewer. For example,
267 * WebView uses this to add its internal layer tree as a child to itself
268 * @hide
269 */
270 public interface HierarchyHandler {
271 /**
272 * Dumps custom children to hierarchy viewer.
John Reckf2361152012-06-14 15:23:22 -0700273 * See ViewDebug.dumpViewWithProperties(Context, View, BufferedWriter, int)
John Reck926cf562012-06-14 10:00:31 -0700274 * for the format
275 *
276 * An empty implementation should simply do nothing
277 *
278 * @param out The output writer
279 * @param level The indentation level
280 */
281 public void dumpViewHierarchyWithProperties(BufferedWriter out, int level);
282
283 /**
284 * Returns a View to enable grabbing screenshots from custom children
285 * returned in dumpViewHierarchyWithProperties.
286 *
287 * @param className The className of the view to find
288 * @param hashCode The hashCode of the view to find
289 * @return the View to capture from, or null if not found
290 */
291 public View findHierarchyView(String className, int hashCode);
292 }
293
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800294 private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
295 private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
296
297 // Maximum delay in ms after which we stop trying to capture a View's drawing
298 private static final int CAPTURE_TIMEOUT = 4000;
299
300 private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
301 private static final String REMOTE_COMMAND_DUMP = "DUMP";
302 private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
303 private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700304 private static final String REMOTE_PROFILE = "PROFILE";
Romain Guy223ff5c2010-03-02 17:07:47 -0800305 private static final String REMOTE_COMMAND_CAPTURE_LAYERS = "CAPTURE_LAYERS";
Chet Haaseed30fd82011-04-22 16:18:45 -0700306 private static final String REMOTE_COMMAND_OUTPUT_DISPLAYLIST = "OUTPUT_DISPLAYLIST";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307
308 private static HashMap<Class<?>, Field[]> sFieldsForClasses;
309 private static HashMap<Class<?>, Method[]> sMethodsForClasses;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700310 private static HashMap<AccessibleObject, ExportedProperty> sAnnotations;
311
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 /**
Romain Guy13b90732012-05-21 12:13:31 -0700313 * @deprecated This enum is now unused
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 */
Romain Guy13b90732012-05-21 12:13:31 -0700315 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 public enum HierarchyTraceType {
317 INVALIDATE,
318 INVALIDATE_CHILD,
319 INVALIDATE_CHILD_IN_PARENT,
320 REQUEST_LAYOUT,
321 ON_LAYOUT,
322 ON_MEASURE,
323 DRAW,
324 BUILD_CACHE
325 }
326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 /**
Romain Guy13b90732012-05-21 12:13:31 -0700328 * @deprecated This enum is now unused
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 */
Romain Guy13b90732012-05-21 12:13:31 -0700330 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 public enum RecyclerTraceType {
332 NEW_VIEW,
333 BIND_VIEW,
334 RECYCLE_FROM_ACTIVE_HEAP,
335 RECYCLE_FROM_SCRAP_HEAP,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 MOVE_TO_SCRAP_HEAP,
337 MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
338 }
339
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 /**
341 * Returns the number of instanciated Views.
342 *
343 * @return The number of Views instanciated in the current process.
344 *
345 * @hide
346 */
347 public static long getViewInstanceCount() {
Brian Carlstromc21550a2010-10-05 21:34:06 -0700348 return Debug.countInstancesOfClass(View.class);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 }
350
351 /**
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700352 * Returns the number of instanciated ViewAncestors.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 *
Joe Onoratoc6cc0f82011-04-12 11:53:13 -0700354 * @return The number of ViewAncestors instanciated in the current process.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 *
356 * @hide
357 */
Romain Guy65b345f2011-07-27 18:51:50 -0700358 public static long getViewRootImplCount() {
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700359 return Debug.countInstancesOfClass(ViewRootImpl.class);
Romain Guya1f3e4a2009-06-04 15:10:46 -0700360 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361
362 /**
Romain Guy13b90732012-05-21 12:13:31 -0700363 * @deprecated This method is now unused and invoking it is a no-op
Romain Guyf9284692011-07-13 18:46:21 -0700364 */
Romain Guy13b90732012-05-21 12:13:31 -0700365 @Deprecated
366 @SuppressWarnings({ "UnusedParameters", "deprecation" })
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 public static void trace(View view, RecyclerTraceType type, int... parameters) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 }
369
370 /**
Romain Guy13b90732012-05-21 12:13:31 -0700371 * @deprecated This method is now unused and invoking it is a no-op
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 */
Romain Guy13b90732012-05-21 12:13:31 -0700373 @Deprecated
374 @SuppressWarnings("UnusedParameters")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375 public static void startRecyclerTracing(String prefix, View view) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 }
377
378 /**
Romain Guy13b90732012-05-21 12:13:31 -0700379 * @deprecated This method is now unused and invoking it is a no-op
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800380 */
Romain Guy13b90732012-05-21 12:13:31 -0700381 @Deprecated
382 @SuppressWarnings("UnusedParameters")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 public static void stopRecyclerTracing() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 }
385
386 /**
Romain Guy13b90732012-05-21 12:13:31 -0700387 * @deprecated This method is now unused and invoking it is a no-op
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 */
Romain Guy13b90732012-05-21 12:13:31 -0700389 @Deprecated
390 @SuppressWarnings({ "UnusedParameters", "deprecation" })
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 public static void trace(View view, HierarchyTraceType type) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 }
393
394 /**
Romain Guy13b90732012-05-21 12:13:31 -0700395 * @deprecated This method is now unused and invoking it is a no-op
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 */
Romain Guy13b90732012-05-21 12:13:31 -0700397 @Deprecated
398 @SuppressWarnings("UnusedParameters")
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 public static void startHierarchyTracing(String prefix, View view) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 }
401
402 /**
Romain Guy13b90732012-05-21 12:13:31 -0700403 * @deprecated This method is now unused and invoking it is a no-op
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 */
Romain Guy13b90732012-05-21 12:13:31 -0700405 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 public static void stopHierarchyTracing() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 }
Romain Guya1f3e4a2009-06-04 15:10:46 -0700408
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 static void dispatchCommand(View view, String command, String parameters,
410 OutputStream clientStream) throws IOException {
411
412 // Paranoid but safe...
413 view = view.getRootView();
414
415 if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
Siva Velusamy945bfb62013-01-06 16:03:12 -0800416 dump(view, false, true, clientStream);
Romain Guy223ff5c2010-03-02 17:07:47 -0800417 } else if (REMOTE_COMMAND_CAPTURE_LAYERS.equalsIgnoreCase(command)) {
418 captureLayers(view, new DataOutputStream(clientStream));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 } else {
420 final String[] params = parameters.split(" ");
421 if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
422 capture(view, clientStream, params[0]);
Chet Haaseed30fd82011-04-22 16:18:45 -0700423 } else if (REMOTE_COMMAND_OUTPUT_DISPLAYLIST.equalsIgnoreCase(command)) {
424 outputDisplayList(view, params[0]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 } else if (REMOTE_COMMAND_INVALIDATE.equalsIgnoreCase(command)) {
426 invalidate(view, params[0]);
427 } else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) {
428 requestLayout(view, params[0]);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700429 } else if (REMOTE_PROFILE.equalsIgnoreCase(command)) {
430 profile(view, clientStream, params[0]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 }
432 }
433 }
434
Siva Velusamy945bfb62013-01-06 16:03:12 -0800435 /** @hide */
436 public static View findView(View root, String parameter) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700437 // Look by type/hashcode
438 if (parameter.indexOf('@') != -1) {
439 final String[] ids = parameter.split("@");
440 final String className = ids[0];
Romain Guy236092a2009-12-14 15:31:48 -0800441 final int hashCode = (int) Long.parseLong(ids[1], 16);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700443 View view = root.getRootView();
444 if (view instanceof ViewGroup) {
445 return findView((ViewGroup) view, className, hashCode);
446 }
447 } else {
448 // Look by id
449 final int id = root.getResources().getIdentifier(parameter, null, null);
450 return root.getRootView().findViewById(id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 }
452
453 return null;
454 }
455
456 private static void invalidate(View root, String parameter) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700457 final View view = findView(root, parameter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 if (view != null) {
459 view.postInvalidate();
460 }
461 }
462
463 private static void requestLayout(View root, String parameter) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700464 final View view = findView(root, parameter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 if (view != null) {
466 root.post(new Runnable() {
467 public void run() {
468 view.requestLayout();
469 }
470 });
471 }
472 }
473
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700474 private static void profile(View root, OutputStream clientStream, String parameter)
475 throws IOException {
476
477 final View view = findView(root, parameter);
478 BufferedWriter out = null;
479 try {
480 out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
481
482 if (view != null) {
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700483 profileViewAndChildren(view, out);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700484 } else {
485 out.write("-1 -1 -1");
486 out.newLine();
487 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700488 out.write("DONE.");
489 out.newLine();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700490 } catch (Exception e) {
491 android.util.Log.w("View", "Problem profiling the view:", e);
492 } finally {
493 if (out != null) {
494 out.close();
495 }
496 }
497 }
498
Siva Velusamy945bfb62013-01-06 16:03:12 -0800499 /** @hide */
500 public static void profileViewAndChildren(final View view, BufferedWriter out)
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700501 throws IOException {
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700502 profileViewAndChildren(view, out, true);
503 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700504
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700505 private static void profileViewAndChildren(final View view, BufferedWriter out, boolean root)
506 throws IOException {
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700507
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700508 long durationMeasure =
Dianne Hackborn4702a852012-08-17 15:18:29 -0700509 (root || (view.mPrivateFlags & View.PFLAG_MEASURED_DIMENSION_SET) != 0)
510 ? profileViewOperation(view, new ViewOperation<Void>() {
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700511 public Void[] pre() {
512 forceLayout(view);
513 return null;
514 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700515
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700516 private void forceLayout(View view) {
517 view.forceLayout();
518 if (view instanceof ViewGroup) {
519 ViewGroup group = (ViewGroup) view;
520 final int count = group.getChildCount();
521 for (int i = 0; i < count; i++) {
522 forceLayout(group.getChildAt(i));
523 }
524 }
525 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700526
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700527 public void run(Void... data) {
528 view.measure(view.mOldWidthMeasureSpec, view.mOldHeightMeasureSpec);
529 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700530
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700531 public void post(Void... data) {
532 }
533 })
534 : 0;
535 long durationLayout =
Dianne Hackborn4702a852012-08-17 15:18:29 -0700536 (root || (view.mPrivateFlags & View.PFLAG_LAYOUT_REQUIRED) != 0)
537 ? profileViewOperation(view, new ViewOperation<Void>() {
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700538 public Void[] pre() {
539 return null;
540 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700541
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700542 public void run(Void... data) {
543 view.layout(view.mLeft, view.mTop, view.mRight, view.mBottom);
544 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700545
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700546 public void post(Void... data) {
547 }
548 }) : 0;
549 long durationDraw =
Dianne Hackborn4702a852012-08-17 15:18:29 -0700550 (root || !view.willNotDraw() || (view.mPrivateFlags & View.PFLAG_DRAWN) != 0)
551 ? profileViewOperation(view, new ViewOperation<Object>() {
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700552 public Object[] pre() {
553 final DisplayMetrics metrics =
Chet Haasedaf98e92011-01-10 14:10:36 -0800554 (view != null && view.getResources() != null) ?
555 view.getResources().getDisplayMetrics() : null;
556 final Bitmap bitmap = metrics != null ?
Dianne Hackborndde331c2012-08-03 14:01:57 -0700557 Bitmap.createBitmap(metrics, metrics.widthPixels,
Chet Haasedaf98e92011-01-10 14:10:36 -0800558 metrics.heightPixels, Bitmap.Config.RGB_565) : null;
559 final Canvas canvas = bitmap != null ? new Canvas(bitmap) : null;
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700560 return new Object[] {
561 bitmap, canvas
562 };
563 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700564
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700565 public void run(Object... data) {
Chet Haasedaf98e92011-01-10 14:10:36 -0800566 if (data[1] != null) {
567 view.draw((Canvas) data[1]);
568 }
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700569 }
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700570
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700571 public void post(Object... data) {
Dianne Hackborn6311d0a2011-08-02 16:37:58 -0700572 if (data[1] != null) {
573 ((Canvas) data[1]).setBitmap(null);
574 }
Chet Haasedaf98e92011-01-10 14:10:36 -0800575 if (data[0] != null) {
576 ((Bitmap) data[0]).recycle();
577 }
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700578 }
579 }) : 0;
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700580 out.write(String.valueOf(durationMeasure));
581 out.write(' ');
582 out.write(String.valueOf(durationLayout));
583 out.write(' ');
584 out.write(String.valueOf(durationDraw));
585 out.newLine();
586 if (view instanceof ViewGroup) {
587 ViewGroup group = (ViewGroup) view;
588 final int count = group.getChildCount();
589 for (int i = 0; i < count; i++) {
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700590 profileViewAndChildren(group.getChildAt(i), out, false);
Konstantin Lopyrevf8e12192010-08-02 19:08:56 -0700591 }
592 }
593 }
594
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700595 interface ViewOperation<T> {
596 T[] pre();
597 void run(T... data);
598 void post(T... data);
599 }
600
601 private static <T> long profileViewOperation(View view, final ViewOperation<T> operation) {
602 final CountDownLatch latch = new CountDownLatch(1);
603 final long[] duration = new long[1];
604
605 view.post(new Runnable() {
606 public void run() {
607 try {
608 T[] data = operation.pre();
609 long start = Debug.threadCpuTimeNanos();
Romain Guy88b4f152011-05-19 16:15:46 -0700610 //noinspection unchecked
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700611 operation.run(data);
612 duration[0] = Debug.threadCpuTimeNanos() - start;
Romain Guy88b4f152011-05-19 16:15:46 -0700613 //noinspection unchecked
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700614 operation.post(data);
615 } finally {
616 latch.countDown();
617 }
618 }
619 });
620
621 try {
Konstantin Lopyrevc6dc4572010-08-06 15:01:52 -0700622 if (!latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS)) {
623 Log.w("View", "Could not complete the profiling of the view " + view);
624 return -1;
625 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700626 } catch (InterruptedException e) {
627 Log.w("View", "Could not complete the profiling of the view " + view);
628 Thread.currentThread().interrupt();
629 return -1;
630 }
631
632 return duration[0];
633 }
634
Siva Velusamy945bfb62013-01-06 16:03:12 -0800635 /** @hide */
636 public static void captureLayers(View root, final DataOutputStream clientStream)
Romain Guy223ff5c2010-03-02 17:07:47 -0800637 throws IOException {
638
639 try {
640 Rect outRect = new Rect();
641 try {
642 root.mAttachInfo.mSession.getDisplayFrame(root.mAttachInfo.mWindow, outRect);
643 } catch (RemoteException e) {
644 // Ignore
645 }
646
647 clientStream.writeInt(outRect.width());
648 clientStream.writeInt(outRect.height());
649
Romain Guy65554f22010-03-22 18:58:21 -0700650 captureViewLayer(root, clientStream, true);
Romain Guy223ff5c2010-03-02 17:07:47 -0800651
652 clientStream.write(2);
653 } finally {
654 clientStream.close();
655 }
656 }
657
Romain Guy65554f22010-03-22 18:58:21 -0700658 private static void captureViewLayer(View view, DataOutputStream clientStream, boolean visible)
Romain Guy223ff5c2010-03-02 17:07:47 -0800659 throws IOException {
660
Romain Guy65554f22010-03-22 18:58:21 -0700661 final boolean localVisible = view.getVisibility() == View.VISIBLE && visible;
662
Dianne Hackborn4702a852012-08-17 15:18:29 -0700663 if ((view.mPrivateFlags & View.PFLAG_SKIP_DRAW) != View.PFLAG_SKIP_DRAW) {
Romain Guy223ff5c2010-03-02 17:07:47 -0800664 final int id = view.getId();
665 String name = view.getClass().getSimpleName();
666 if (id != View.NO_ID) {
667 name = resolveId(view.getContext(), id).toString();
668 }
669
670 clientStream.write(1);
671 clientStream.writeUTF(name);
Romain Guy65554f22010-03-22 18:58:21 -0700672 clientStream.writeByte(localVisible ? 1 : 0);
Romain Guy223ff5c2010-03-02 17:07:47 -0800673
674 int[] position = new int[2];
675 // XXX: Should happen on the UI thread
676 view.getLocationInWindow(position);
677
678 clientStream.writeInt(position[0]);
679 clientStream.writeInt(position[1]);
680 clientStream.flush();
681
682 Bitmap b = performViewCapture(view, true);
683 if (b != null) {
684 ByteArrayOutputStream arrayOut = new ByteArrayOutputStream(b.getWidth() *
685 b.getHeight() * 2);
686 b.compress(Bitmap.CompressFormat.PNG, 100, arrayOut);
687 clientStream.writeInt(arrayOut.size());
688 arrayOut.writeTo(clientStream);
689 }
690 clientStream.flush();
691 }
692
693 if (view instanceof ViewGroup) {
694 ViewGroup group = (ViewGroup) view;
695 int count = group.getChildCount();
696
697 for (int i = 0; i < count; i++) {
Romain Guy65554f22010-03-22 18:58:21 -0700698 captureViewLayer(group.getChildAt(i), clientStream, localVisible);
Romain Guy223ff5c2010-03-02 17:07:47 -0800699 }
700 }
Chet Haase68bf5bd2013-09-05 16:27:28 -0700701
702 if (view.mOverlay != null) {
703 ViewGroup overlayContainer = view.getOverlay().mOverlayViewGroup;
704 captureViewLayer(overlayContainer, clientStream, localVisible);
705 }
Romain Guy223ff5c2010-03-02 17:07:47 -0800706 }
707
Chet Haaseed30fd82011-04-22 16:18:45 -0700708 private static void outputDisplayList(View root, String parameter) throws IOException {
709 final View view = findView(root, parameter);
Dianne Hackborn6dd005b2011-07-18 13:22:50 -0700710 view.getViewRootImpl().outputDisplayList(view);
Chet Haaseed30fd82011-04-22 16:18:45 -0700711 }
712
Siva Velusamy945bfb62013-01-06 16:03:12 -0800713 /** @hide */
714 public static void outputDisplayList(View root, View target) {
715 root.getViewRootImpl().outputDisplayList(target);
716 }
717
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 private static void capture(View root, final OutputStream clientStream, String parameter)
719 throws IOException {
720
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700721 final View captureView = findView(root, parameter);
Siva Velusamy945bfb62013-01-06 16:03:12 -0800722 capture(root, clientStream, captureView);
723 }
724
725 /** @hide */
726 public static void capture(View root, final OutputStream clientStream, View captureView)
727 throws IOException {
Romain Guy223ff5c2010-03-02 17:07:47 -0800728 Bitmap b = performViewCapture(captureView, false);
Konstantin Lopyrev43b9b482010-08-24 22:00:12 -0700729
730 if (b == null) {
Romain Guy223ff5c2010-03-02 17:07:47 -0800731 Log.w("View", "Failed to create capture bitmap!");
Konstantin Lopyrev43b9b482010-08-24 22:00:12 -0700732 // Send an empty one so that it doesn't get stuck waiting for
733 // something.
Dianne Hackborndde331c2012-08-03 14:01:57 -0700734 b = Bitmap.createBitmap(root.getResources().getDisplayMetrics(),
735 1, 1, Bitmap.Config.ARGB_8888);
Konstantin Lopyrev43b9b482010-08-24 22:00:12 -0700736 }
737
738 BufferedOutputStream out = null;
739 try {
740 out = new BufferedOutputStream(clientStream, 32 * 1024);
741 b.compress(Bitmap.CompressFormat.PNG, 100, out);
742 out.flush();
743 } finally {
744 if (out != null) {
745 out.close();
746 }
747 b.recycle();
Romain Guy223ff5c2010-03-02 17:07:47 -0800748 }
749 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750
Chet Haase68bf5bd2013-09-05 16:27:28 -0700751 private static Bitmap performViewCapture(final View captureView, final boolean skipChildren) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 if (captureView != null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700753 final CountDownLatch latch = new CountDownLatch(1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800754 final Bitmap[] cache = new Bitmap[1];
755
Romain Guy223ff5c2010-03-02 17:07:47 -0800756 captureView.post(new Runnable() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800757 public void run() {
758 try {
Dianne Hackborn958b9ad2009-03-31 18:00:36 -0700759 cache[0] = captureView.createSnapshot(
Chet Haase68bf5bd2013-09-05 16:27:28 -0700760 Bitmap.Config.ARGB_8888, 0, skipChildren);
Dianne Hackborn958b9ad2009-03-31 18:00:36 -0700761 } catch (OutOfMemoryError e) {
Romain Guy88b4f152011-05-19 16:15:46 -0700762 Log.w("View", "Out of memory for bitmap");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 } finally {
764 latch.countDown();
765 }
766 }
767 });
768
769 try {
770 latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
Romain Guy223ff5c2010-03-02 17:07:47 -0800771 return cache[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 } catch (InterruptedException e) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700773 Log.w("View", "Could not complete the capture of the view " + captureView);
774 Thread.currentThread().interrupt();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 }
776 }
Romain Guy223ff5c2010-03-02 17:07:47 -0800777
778 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 }
780
Siva Velusamy945bfb62013-01-06 16:03:12 -0800781 /**
782 * Dumps the view hierarchy starting from the given view.
783 * @hide
784 */
785 public static void dump(View root, boolean skipChildren, boolean includeProperties,
786 OutputStream clientStream) throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 BufferedWriter out = null;
788 try {
Romain Guy38e951b2009-12-17 13:28:30 -0800789 out = new BufferedWriter(new OutputStreamWriter(clientStream, "utf-8"), 32 * 1024);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800790 View view = root.getRootView();
791 if (view instanceof ViewGroup) {
792 ViewGroup group = (ViewGroup) view;
Siva Velusamy945bfb62013-01-06 16:03:12 -0800793 dumpViewHierarchy(group.getContext(), group, out, 0,
794 skipChildren, includeProperties);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 }
796 out.write("DONE.");
797 out.newLine();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700798 } catch (Exception e) {
799 android.util.Log.w("View", "Problem dumping the view:", e);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800800 } finally {
801 if (out != null) {
802 out.close();
803 }
804 }
805 }
806
807 private static View findView(ViewGroup group, String className, int hashCode) {
808 if (isRequestedView(group, className, hashCode)) {
809 return group;
810 }
811
812 final int count = group.getChildCount();
813 for (int i = 0; i < count; i++) {
814 final View view = group.getChildAt(i);
815 if (view instanceof ViewGroup) {
816 final View found = findView((ViewGroup) view, className, hashCode);
817 if (found != null) {
818 return found;
819 }
820 } else if (isRequestedView(view, className, hashCode)) {
821 return view;
822 }
Chet Haase68bf5bd2013-09-05 16:27:28 -0700823 if (view.mOverlay != null) {
824 final View found = findView((ViewGroup) view.mOverlay.mOverlayViewGroup,
825 className, hashCode);
826 if (found != null) {
827 return found;
828 }
829 }
John Reck926cf562012-06-14 10:00:31 -0700830 if (view instanceof HierarchyHandler) {
831 final View found = ((HierarchyHandler)view)
832 .findHierarchyView(className, hashCode);
833 if (found != null) {
834 return found;
835 }
836 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 return null;
839 }
840
841 private static boolean isRequestedView(View view, String className, int hashCode) {
Chet Haase68bf5bd2013-09-05 16:27:28 -0700842 if (view.hashCode() == hashCode) {
843 String viewClassName = view.getClass().getName();
844 if (className.equals("ViewOverlay")) {
845 return viewClassName.equals("android.view.ViewOverlay$OverlayViewGroup");
846 } else {
847 return className.equals(viewClassName);
848 }
849 }
850 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 }
852
Siva Velusamy945bfb62013-01-06 16:03:12 -0800853 private static void dumpViewHierarchy(Context context, ViewGroup group,
854 BufferedWriter out, int level, boolean skipChildren, boolean includeProperties) {
855 if (!dumpView(context, group, out, level, includeProperties)) {
856 return;
857 }
858
859 if (skipChildren) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 return;
861 }
862
863 final int count = group.getChildCount();
864 for (int i = 0; i < count; i++) {
865 final View view = group.getChildAt(i);
866 if (view instanceof ViewGroup) {
Siva Velusamy945bfb62013-01-06 16:03:12 -0800867 dumpViewHierarchy(context, (ViewGroup) view, out, level + 1, skipChildren,
868 includeProperties);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 } else {
Siva Velusamy945bfb62013-01-06 16:03:12 -0800870 dumpView(context, view, out, level + 1, includeProperties);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 }
Chet Haase68bf5bd2013-09-05 16:27:28 -0700872 if (view.mOverlay != null) {
873 ViewOverlay overlay = view.getOverlay();
874 ViewGroup overlayContainer = overlay.mOverlayViewGroup;
875 dumpViewHierarchy(context, overlayContainer, out, level + 2, skipChildren,
876 includeProperties);
877 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 }
John Reck926cf562012-06-14 10:00:31 -0700879 if (group instanceof HierarchyHandler) {
880 ((HierarchyHandler)group).dumpViewHierarchyWithProperties(out, level + 1);
881 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 }
883
Siva Velusamy945bfb62013-01-06 16:03:12 -0800884 private static boolean dumpView(Context context, View view,
885 BufferedWriter out, int level, boolean includeProperties) {
The Android Open Source Project10592532009-03-18 17:39:46 -0700886
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 try {
888 for (int i = 0; i < level; i++) {
889 out.write(' ');
890 }
Chet Haase68bf5bd2013-09-05 16:27:28 -0700891 String className = view.getClass().getName();
892 if (className.equals("android.view.ViewOverlay$OverlayViewGroup")) {
893 className = "ViewOverlay";
894 }
895 out.write(className);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 out.write('@');
897 out.write(Integer.toHexString(view.hashCode()));
898 out.write(' ');
Siva Velusamy945bfb62013-01-06 16:03:12 -0800899 if (includeProperties) {
900 dumpViewProperties(context, view, out);
901 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 out.newLine();
903 } catch (IOException e) {
904 Log.w("View", "Error while dumping hierarchy tree");
905 return false;
906 }
907 return true;
908 }
909
910 private static Field[] getExportedPropertyFields(Class<?> klass) {
911 if (sFieldsForClasses == null) {
912 sFieldsForClasses = new HashMap<Class<?>, Field[]>();
913 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700914 if (sAnnotations == null) {
915 sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
916 }
917
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
919
920 Field[] fields = map.get(klass);
921 if (fields != null) {
922 return fields;
923 }
924
925 final ArrayList<Field> foundFields = new ArrayList<Field>();
926 fields = klass.getDeclaredFields();
927
928 int count = fields.length;
929 for (int i = 0; i < count; i++) {
930 final Field field = fields[i];
931 if (field.isAnnotationPresent(ExportedProperty.class)) {
932 field.setAccessible(true);
933 foundFields.add(field);
Romain Guy88b4f152011-05-19 16:15:46 -0700934 sAnnotations.put(field, field.getAnnotation(ExportedProperty.class));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 }
936 }
937
938 fields = foundFields.toArray(new Field[foundFields.size()]);
939 map.put(klass, fields);
940
941 return fields;
942 }
943
944 private static Method[] getExportedPropertyMethods(Class<?> klass) {
945 if (sMethodsForClasses == null) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700946 sMethodsForClasses = new HashMap<Class<?>, Method[]>(100);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700948 if (sAnnotations == null) {
949 sAnnotations = new HashMap<AccessibleObject, ExportedProperty>(512);
950 }
951
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
953
954 Method[] methods = map.get(klass);
955 if (methods != null) {
956 return methods;
957 }
958
959 final ArrayList<Method> foundMethods = new ArrayList<Method>();
960 methods = klass.getDeclaredMethods();
Romain Guya1f3e4a2009-06-04 15:10:46 -0700961
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 int count = methods.length;
963 for (int i = 0; i < count; i++) {
Romain Guya1f3e4a2009-06-04 15:10:46 -0700964 final Method method = methods[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 if (method.getParameterTypes().length == 0 &&
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700966 method.isAnnotationPresent(ExportedProperty.class) &&
967 method.getReturnType() != Void.class) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 method.setAccessible(true);
969 foundMethods.add(method);
Romain Guy88b4f152011-05-19 16:15:46 -0700970 sAnnotations.put(method, method.getAnnotation(ExportedProperty.class));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 }
972 }
973
974 methods = foundMethods.toArray(new Method[foundMethods.size()]);
975 map.put(klass, methods);
976
977 return methods;
978 }
979
The Android Open Source Project10592532009-03-18 17:39:46 -0700980 private static void dumpViewProperties(Context context, Object view,
981 BufferedWriter out) throws IOException {
982
983 dumpViewProperties(context, view, out, "");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 }
985
The Android Open Source Project10592532009-03-18 17:39:46 -0700986 private static void dumpViewProperties(Context context, Object view,
987 BufferedWriter out, String prefix) throws IOException {
988
Romain Guydfab3632012-10-03 14:53:25 -0700989 if (view == null) {
990 out.write(prefix + "=4,null ");
991 return;
992 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800993
Romain Guydfab3632012-10-03 14:53:25 -0700994 Class<?> klass = view.getClass();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 do {
The Android Open Source Project10592532009-03-18 17:39:46 -0700996 exportFields(context, view, out, klass, prefix);
997 exportMethods(context, view, out, klass, prefix);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 klass = klass.getSuperclass();
999 } while (klass != Object.class);
1000 }
Romain Guya1f3e4a2009-06-04 15:10:46 -07001001
Kristian Monsen97e8f622013-04-26 12:51:19 -07001002 private static Object callMethodOnAppropriateTheadBlocking(final Method method,
1003 final Object object) throws IllegalAccessException, InvocationTargetException,
1004 TimeoutException {
1005 if (!(object instanceof View)) {
1006 return method.invoke(object, (Object[]) null);
1007 }
1008
1009 final View view = (View) object;
1010 Callable<Object> callable = new Callable<Object>() {
1011 @Override
1012 public Object call() throws IllegalAccessException, InvocationTargetException {
1013 return method.invoke(view, (Object[]) null);
1014 }
1015 };
1016 FutureTask<Object> future = new FutureTask<Object>(callable);
1017 // Try to use the handler provided by the view
1018 Handler handler = view.getHandler();
1019 // Fall back on using the main thread
1020 if (handler == null) {
1021 handler = new Handler(android.os.Looper.getMainLooper());
1022 }
1023 handler.post(future);
1024 while (true) {
1025 try {
1026 return future.get(CAPTURE_TIMEOUT, java.util.concurrent.TimeUnit.MILLISECONDS);
1027 } catch (ExecutionException e) {
1028 Throwable t = e.getCause();
1029 if (t instanceof IllegalAccessException) {
1030 throw (IllegalAccessException)t;
1031 }
1032 if (t instanceof InvocationTargetException) {
1033 throw (InvocationTargetException)t;
1034 }
1035 throw new RuntimeException("Unexpected exception", t);
1036 } catch (InterruptedException e) {
1037 // Call get again
1038 } catch (CancellationException e) {
1039 throw new RuntimeException("Unexpected cancellation exception", e);
1040 }
1041 }
1042 }
1043
The Android Open Source Project10592532009-03-18 17:39:46 -07001044 private static void exportMethods(Context context, Object view, BufferedWriter out,
1045 Class<?> klass, String prefix) throws IOException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046
1047 final Method[] methods = getExportedPropertyMethods(klass);
1048
1049 int count = methods.length;
1050 for (int i = 0; i < count; i++) {
1051 final Method method = methods[i];
1052 //noinspection EmptyCatchBlock
1053 try {
Kristian Monsen97e8f622013-04-26 12:51:19 -07001054 Object methodValue = callMethodOnAppropriateTheadBlocking(method, view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 final Class<?> returnType = method.getReturnType();
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001056 final ExportedProperty property = sAnnotations.get(method);
Konstantin Lopyrev91a7f5f2010-08-10 18:54:54 -07001057 String categoryPrefix =
1058 property.category().length() != 0 ? property.category() + ":" : "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059
1060 if (returnType == int.class) {
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001061
The Android Open Source Project10592532009-03-18 17:39:46 -07001062 if (property.resolveId() && context != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 final int id = (Integer) methodValue;
The Android Open Source Project10592532009-03-18 17:39:46 -07001064 methodValue = resolveId(context, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001065 } else {
Romain Guy809a7f62009-05-14 15:44:42 -07001066 final FlagToString[] flagsMapping = property.flagMapping();
1067 if (flagsMapping.length > 0) {
1068 final int intValue = (Integer) methodValue;
Konstantin Lopyrev91a7f5f2010-08-10 18:54:54 -07001069 final String valuePrefix =
1070 categoryPrefix + prefix + method.getName() + '_';
Romain Guy809a7f62009-05-14 15:44:42 -07001071 exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
1072 }
1073
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 final IntToString[] mapping = property.mapping();
1075 if (mapping.length > 0) {
1076 final int intValue = (Integer) methodValue;
1077 boolean mapped = false;
1078 int mappingCount = mapping.length;
1079 for (int j = 0; j < mappingCount; j++) {
1080 final IntToString mapper = mapping[j];
1081 if (mapper.from() == intValue) {
1082 methodValue = mapper.to();
1083 mapped = true;
1084 break;
1085 }
1086 }
1087
1088 if (!mapped) {
1089 methodValue = intValue;
1090 }
1091 }
1092 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001093 } else if (returnType == int[].class) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001094 final int[] array = (int[]) methodValue;
Konstantin Lopyrev91a7f5f2010-08-10 18:54:54 -07001095 final String valuePrefix = categoryPrefix + prefix + method.getName() + '_';
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001096 final String suffix = "()";
1097
The Android Open Source Project10592532009-03-18 17:39:46 -07001098 exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001099
1100 // Probably want to return here, same as for fields.
1101 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 } else if (!returnType.isPrimitive()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 if (property.deepExport()) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001104 dumpViewProperties(context, methodValue, out, prefix + property.prefix());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 continue;
1106 }
1107 }
1108
Konstantin Lopyrev91a7f5f2010-08-10 18:54:54 -07001109 writeEntry(out, categoryPrefix + prefix, method.getName(), "()", methodValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 } catch (IllegalAccessException e) {
1111 } catch (InvocationTargetException e) {
Kristian Monsen97e8f622013-04-26 12:51:19 -07001112 } catch (TimeoutException e) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 }
1114 }
1115 }
1116
The Android Open Source Project10592532009-03-18 17:39:46 -07001117 private static void exportFields(Context context, Object view, BufferedWriter out,
1118 Class<?> klass, String prefix) throws IOException {
1119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 final Field[] fields = getExportedPropertyFields(klass);
1121
1122 int count = fields.length;
1123 for (int i = 0; i < count; i++) {
1124 final Field field = fields[i];
1125
1126 //noinspection EmptyCatchBlock
1127 try {
1128 Object fieldValue = null;
1129 final Class<?> type = field.getType();
Konstantin Lopyrevbea95162010-08-10 17:02:18 -07001130 final ExportedProperty property = sAnnotations.get(field);
Konstantin Lopyrev91a7f5f2010-08-10 18:54:54 -07001131 String categoryPrefix =
1132 property.category().length() != 0 ? property.category() + ":" : "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133
Fabrice Di Megliob365f912013-03-27 16:36:21 -07001134 if (type == int.class || type == byte.class) {
The Android Open Source Project10592532009-03-18 17:39:46 -07001135 if (property.resolveId() && context != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001136 final int id = field.getInt(view);
The Android Open Source Project10592532009-03-18 17:39:46 -07001137 fieldValue = resolveId(context, id);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 } else {
Romain Guy809a7f62009-05-14 15:44:42 -07001139 final FlagToString[] flagsMapping = property.flagMapping();
1140 if (flagsMapping.length > 0) {
1141 final int intValue = field.getInt(view);
Konstantin Lopyrev91a7f5f2010-08-10 18:54:54 -07001142 final String valuePrefix =
1143 categoryPrefix + prefix + field.getName() + '_';
Romain Guy809a7f62009-05-14 15:44:42 -07001144 exportUnrolledFlags(out, flagsMapping, intValue, valuePrefix);
1145 }
1146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 final IntToString[] mapping = property.mapping();
1148 if (mapping.length > 0) {
1149 final int intValue = field.getInt(view);
1150 int mappingCount = mapping.length;
1151 for (int j = 0; j < mappingCount; j++) {
1152 final IntToString mapped = mapping[j];
1153 if (mapped.from() == intValue) {
1154 fieldValue = mapped.to();
1155 break;
1156 }
1157 }
1158
1159 if (fieldValue == null) {
1160 fieldValue = intValue;
1161 }
1162 }
1163 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001164 } else if (type == int[].class) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001165 final int[] array = (int[]) field.get(view);
Konstantin Lopyrev91a7f5f2010-08-10 18:54:54 -07001166 final String valuePrefix = categoryPrefix + prefix + field.getName() + '_';
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001167 final String suffix = "";
1168
The Android Open Source Project10592532009-03-18 17:39:46 -07001169 exportUnrolledArray(context, out, property, array, valuePrefix, suffix);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001170
1171 // We exit here!
1172 return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 } else if (!type.isPrimitive()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 if (property.deepExport()) {
Romain Guydfab3632012-10-03 14:53:25 -07001175 dumpViewProperties(context, field.get(view), out, prefix +
1176 property.prefix());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 continue;
1178 }
1179 }
1180
1181 if (fieldValue == null) {
1182 fieldValue = field.get(view);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 }
1184
Konstantin Lopyrev91a7f5f2010-08-10 18:54:54 -07001185 writeEntry(out, categoryPrefix + prefix, field.getName(), "", fieldValue);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 } catch (IllegalAccessException e) {
1187 }
1188 }
1189 }
1190
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001191 private static void writeEntry(BufferedWriter out, String prefix, String name,
1192 String suffix, Object value) throws IOException {
1193
1194 out.write(prefix);
1195 out.write(name);
1196 out.write(suffix);
1197 out.write("=");
1198 writeValue(out, value);
1199 out.write(' ');
1200 }
1201
Romain Guy809a7f62009-05-14 15:44:42 -07001202 private static void exportUnrolledFlags(BufferedWriter out, FlagToString[] mapping,
1203 int intValue, String prefix) throws IOException {
1204
1205 final int count = mapping.length;
1206 for (int j = 0; j < count; j++) {
1207 final FlagToString flagMapping = mapping[j];
1208 final boolean ifTrue = flagMapping.outputIf();
Romain Guy5bcdff42009-05-14 21:27:18 -07001209 final int maskResult = intValue & flagMapping.mask();
1210 final boolean test = maskResult == flagMapping.equals();
Romain Guy809a7f62009-05-14 15:44:42 -07001211 if ((test && ifTrue) || (!test && !ifTrue)) {
1212 final String name = flagMapping.name();
Romain Guy5bcdff42009-05-14 21:27:18 -07001213 final String value = "0x" + Integer.toHexString(maskResult);
Romain Guy809a7f62009-05-14 15:44:42 -07001214 writeEntry(out, prefix, name, "", value);
1215 }
1216 }
1217 }
1218
The Android Open Source Project10592532009-03-18 17:39:46 -07001219 private static void exportUnrolledArray(Context context, BufferedWriter out,
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001220 ExportedProperty property, int[] array, String prefix, String suffix)
1221 throws IOException {
1222
1223 final IntToString[] indexMapping = property.indexMapping();
1224 final boolean hasIndexMapping = indexMapping.length > 0;
1225
1226 final IntToString[] mapping = property.mapping();
1227 final boolean hasMapping = mapping.length > 0;
1228
The Android Open Source Project10592532009-03-18 17:39:46 -07001229 final boolean resolveId = property.resolveId() && context != null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001230 final int valuesCount = array.length;
1231
1232 for (int j = 0; j < valuesCount; j++) {
1233 String name;
Romain Guya1f3e4a2009-06-04 15:10:46 -07001234 String value = null;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001235
1236 final int intValue = array[j];
1237
1238 name = String.valueOf(j);
1239 if (hasIndexMapping) {
1240 int mappingCount = indexMapping.length;
1241 for (int k = 0; k < mappingCount; k++) {
1242 final IntToString mapped = indexMapping[k];
1243 if (mapped.from() == j) {
1244 name = mapped.to();
1245 break;
1246 }
1247 }
1248 }
1249
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001250 if (hasMapping) {
1251 int mappingCount = mapping.length;
1252 for (int k = 0; k < mappingCount; k++) {
1253 final IntToString mapped = mapping[k];
1254 if (mapped.from() == intValue) {
1255 value = mapped.to();
1256 break;
1257 }
1258 }
1259 }
1260
1261 if (resolveId) {
Romain Guya1f3e4a2009-06-04 15:10:46 -07001262 if (value == null) value = (String) resolveId(context, intValue);
1263 } else {
1264 value = String.valueOf(intValue);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001265 }
1266
1267 writeEntry(out, prefix, name, suffix, value);
1268 }
1269 }
1270
Romain Guy237c1ce2009-12-08 11:30:25 -08001271 static Object resolveId(Context context, int id) {
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001272 Object fieldValue;
The Android Open Source Project10592532009-03-18 17:39:46 -07001273 final Resources resources = context.getResources();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001274 if (id >= 0) {
1275 try {
1276 fieldValue = resources.getResourceTypeName(id) + '/' +
1277 resources.getResourceEntryName(id);
1278 } catch (Resources.NotFoundException e) {
1279 fieldValue = "id/0x" + Integer.toHexString(id);
1280 }
1281 } else {
1282 fieldValue = "NO_ID";
1283 }
1284 return fieldValue;
1285 }
1286
1287 private static void writeValue(BufferedWriter out, Object value) throws IOException {
1288 if (value != null) {
Romain Guy97723b22012-09-27 23:36:34 -07001289 String output = "[EXCEPTION]";
1290 try {
1291 output = value.toString().replace("\n", "\\n");
1292 } finally {
1293 out.write(String.valueOf(output.length()));
1294 out.write(",");
1295 out.write(output);
1296 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001297 } else {
1298 out.write("4,null");
1299 }
1300 }
1301
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
1303 if (mCapturedViewFieldsForClasses == null) {
1304 mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
1305 }
1306 final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
1307
1308 Field[] fields = map.get(klass);
1309 if (fields != null) {
1310 return fields;
1311 }
1312
1313 final ArrayList<Field> foundFields = new ArrayList<Field>();
1314 fields = klass.getFields();
1315
1316 int count = fields.length;
1317 for (int i = 0; i < count; i++) {
1318 final Field field = fields[i];
1319 if (field.isAnnotationPresent(CapturedViewProperty.class)) {
1320 field.setAccessible(true);
1321 foundFields.add(field);
1322 }
1323 }
1324
1325 fields = foundFields.toArray(new Field[foundFields.size()]);
1326 map.put(klass, fields);
1327
1328 return fields;
1329 }
1330
1331 private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
1332 if (mCapturedViewMethodsForClasses == null) {
1333 mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
1334 }
1335 final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
1336
1337 Method[] methods = map.get(klass);
1338 if (methods != null) {
1339 return methods;
1340 }
1341
1342 final ArrayList<Method> foundMethods = new ArrayList<Method>();
1343 methods = klass.getMethods();
Romain Guya1f3e4a2009-06-04 15:10:46 -07001344
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 int count = methods.length;
1346 for (int i = 0; i < count; i++) {
Romain Guya1f3e4a2009-06-04 15:10:46 -07001347 final Method method = methods[i];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001348 if (method.getParameterTypes().length == 0 &&
1349 method.isAnnotationPresent(CapturedViewProperty.class) &&
1350 method.getReturnType() != Void.class) {
1351 method.setAccessible(true);
1352 foundMethods.add(method);
1353 }
1354 }
1355
1356 methods = foundMethods.toArray(new Method[foundMethods.size()]);
1357 map.put(klass, methods);
1358
1359 return methods;
1360 }
Romain Guya1f3e4a2009-06-04 15:10:46 -07001361
1362 private static String capturedViewExportMethods(Object obj, Class<?> klass,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 String prefix) {
1364
1365 if (obj == null) {
1366 return "null";
1367 }
Romain Guya1f3e4a2009-06-04 15:10:46 -07001368
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001369 StringBuilder sb = new StringBuilder();
1370 final Method[] methods = capturedViewGetPropertyMethods(klass);
1371
1372 int count = methods.length;
1373 for (int i = 0; i < count; i++) {
1374 final Method method = methods[i];
1375 try {
1376 Object methodValue = method.invoke(obj, (Object[]) null);
1377 final Class<?> returnType = method.getReturnType();
Romain Guya1f3e4a2009-06-04 15:10:46 -07001378
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001379 CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
1380 if (property.retrieveReturn()) {
1381 //we are interested in the second level data only
1382 sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
Romain Guya1f3e4a2009-06-04 15:10:46 -07001383 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 sb.append(prefix);
1385 sb.append(method.getName());
1386 sb.append("()=");
Romain Guya1f3e4a2009-06-04 15:10:46 -07001387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 if (methodValue != null) {
Romain Guya1f3e4a2009-06-04 15:10:46 -07001389 final String value = methodValue.toString().replace("\n", "\\n");
1390 sb.append(value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001391 } else {
1392 sb.append("null");
1393 }
1394 sb.append("; ");
1395 }
1396 } catch (IllegalAccessException e) {
Romain Guya1f3e4a2009-06-04 15:10:46 -07001397 //Exception IllegalAccess, it is OK here
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001398 //we simply ignore this method
1399 } catch (InvocationTargetException e) {
Romain Guya1f3e4a2009-06-04 15:10:46 -07001400 //Exception InvocationTarget, it is OK here
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 //we simply ignore this method
Romain Guya1f3e4a2009-06-04 15:10:46 -07001402 }
1403 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 return sb.toString();
1405 }
1406
1407 private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001408 if (obj == null) {
1409 return "null";
1410 }
Romain Guya1f3e4a2009-06-04 15:10:46 -07001411
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001412 StringBuilder sb = new StringBuilder();
1413 final Field[] fields = capturedViewGetPropertyFields(klass);
1414
1415 int count = fields.length;
1416 for (int i = 0; i < count; i++) {
1417 final Field field = fields[i];
1418 try {
1419 Object fieldValue = field.get(obj);
1420
1421 sb.append(prefix);
1422 sb.append(field.getName());
1423 sb.append("=");
1424
1425 if (fieldValue != null) {
1426 final String value = fieldValue.toString().replace("\n", "\\n");
1427 sb.append(value);
1428 } else {
1429 sb.append("null");
1430 }
1431 sb.append(' ');
1432 } catch (IllegalAccessException e) {
Romain Guya1f3e4a2009-06-04 15:10:46 -07001433 //Exception IllegalAccess, it is OK here
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001434 //we simply ignore this field
1435 }
1436 }
1437 return sb.toString();
1438 }
Romain Guya1f3e4a2009-06-04 15:10:46 -07001439
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001440 /**
Romain Guya1f3e4a2009-06-04 15:10:46 -07001441 * Dump view info for id based instrument test generation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 * (and possibly further data analysis). The results are dumped
Romain Guya1f3e4a2009-06-04 15:10:46 -07001443 * to the log.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 * @param tag for log
1445 * @param view for dump
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 */
Romain Guya1f3e4a2009-06-04 15:10:46 -07001447 public static void dumpCapturedView(String tag, Object view) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001448 Class<?> klass = view.getClass();
1449 StringBuilder sb = new StringBuilder(klass.getName() + ": ");
1450 sb.append(capturedViewExportFields(view, klass, ""));
Romain Guya1f3e4a2009-06-04 15:10:46 -07001451 sb.append(capturedViewExportMethods(view, klass, ""));
1452 Log.d(tag, sb.toString());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 }
Siva Velusamyf9455fa2013-01-17 18:01:52 -08001454
1455 /**
1456 * Invoke a particular method on given view.
1457 * The given method is always invoked on the UI thread. The caller thread will stall until the
1458 * method invocation is complete. Returns an object equal to the result of the method
1459 * invocation, null if the method is declared to return void
1460 * @throws Exception if the method invocation caused any exception
1461 * @hide
1462 */
1463 public static Object invokeViewMethod(final View view, final Method method,
1464 final Object[] args) {
1465 final CountDownLatch latch = new CountDownLatch(1);
1466 final AtomicReference<Object> result = new AtomicReference<Object>();
1467 final AtomicReference<Throwable> exception = new AtomicReference<Throwable>();
1468
1469 view.post(new Runnable() {
1470 @Override
1471 public void run() {
1472 try {
1473 result.set(method.invoke(view, args));
1474 } catch (InvocationTargetException e) {
1475 exception.set(e.getCause());
1476 } catch (Exception e) {
1477 exception.set(e);
1478 }
1479
1480 latch.countDown();
1481 }
1482 });
1483
1484 try {
1485 latch.await();
1486 } catch (InterruptedException e) {
1487 throw new RuntimeException(e);
1488 }
1489
1490 if (exception.get() != null) {
1491 throw new RuntimeException(exception.get());
1492 }
1493
1494 return result.get();
1495 }
1496
1497 /**
1498 * @hide
1499 */
1500 public static void setLayoutParameter(final View view, final String param, final int value)
1501 throws NoSuchFieldException, IllegalAccessException {
1502 final ViewGroup.LayoutParams p = view.getLayoutParams();
1503 final Field f = p.getClass().getField(param);
1504 if (f.getType() != int.class) {
1505 throw new RuntimeException("Only integer layout parameters can be set. Field "
1506 + param + " is of type " + f.getType().getSimpleName());
1507 }
1508
1509 f.set(p, Integer.valueOf(value));
1510
1511 view.post(new Runnable() {
1512 @Override
1513 public void run() {
1514 view.setLayoutParams(p);
1515 }
1516 });
1517 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001518}