@@ -29,10 +29,10 @@ public final class SnapshotInstrumentation extends Instrumentation {
2929 private static final String OUTPUT_FORMAT = "uiautomator-xml" ;
3030 private static final String HELPER_API_VERSION = "1" ;
3131 private static final int CHUNK_SIZE = 2 * 1024 ;
32- // Keep the default quiet window short: RN/animation-heavy apps often never become fully idle,
33- // and callers can still override this for alert-style flows that need a longer settle period .
34- private static final long DEFAULT_WAIT_FOR_IDLE_TIMEOUT_MS = 25 ;
35- private static final long DEFAULT_WAIT_FOR_IDLE_QUIET_MS = 25 ;
32+ // Match the host defaults: long enough to avoid mid-transition RN snapshots, but still bounded
33+ // below the stock uiautomator idle wait so busy apps do not stall every capture .
34+ private static final long DEFAULT_WAIT_FOR_IDLE_TIMEOUT_MS = 500 ;
35+ private static final long DEFAULT_WAIT_FOR_IDLE_QUIET_MS = 100 ;
3636 private static final long DEFAULT_TIMEOUT_MS = 8_000 ;
3737 private static final int DEFAULT_MAX_DEPTH = 128 ;
3838 private static final int DEFAULT_MAX_NODES = 5_000 ;
@@ -351,7 +351,7 @@ private CaptureResult captureXml(
351351 AccessibilityNodeInfo root = automation .getRootInActiveWindow ();
352352 try {
353353 if (root != null ) {
354- appendNode (xml , root , 0 , 0 , maxDepth , maxNodes , stats );
354+ appendNode (xml , root , 0 , 0 , maxDepth , maxNodes , stats , null );
355355 windowCount = 1 ;
356356 }
357357 captureMode = "active-window" ;
@@ -439,7 +439,15 @@ private static int appendInteractiveWindowRoots(
439439 }
440440 StringBuilder windowXml = new StringBuilder ();
441441 CaptureStats windowStats = stats .copy ();
442- appendNode (windowXml , root , windowCount , 0 , maxDepth , maxNodes , windowStats );
442+ appendNode (
443+ windowXml ,
444+ root ,
445+ windowCount ,
446+ 0 ,
447+ maxDepth ,
448+ maxNodes ,
449+ windowStats ,
450+ readWindowMetadata (window , windowCount ));
443451 xml .append (windowXml );
444452 stats .copyFrom (windowStats );
445453 windowCount += 1 ;
@@ -482,7 +490,8 @@ private static void appendNode(
482490 int depth ,
483491 int maxDepth ,
484492 int maxNodes ,
485- CaptureStats stats ) {
493+ CaptureStats stats ,
494+ WindowMetadata windowMetadata ) {
486495 if (stats .nodeCount >= maxNodes ) {
487496 stats .truncated = true ;
488497 return ;
@@ -495,11 +504,15 @@ private static void appendNode(
495504 // without affecting current snapshot semantics; add fields back here when TS starts reading
496505 // them.
497506 appendAttribute (xml , "index" , Integer .toString (nodeIndex ));
507+ if (windowMetadata != null ) {
508+ appendWindowMetadata (xml , windowMetadata );
509+ }
498510 appendNonEmptyAttribute (xml , "text" , node .getText ());
499511 appendNonEmptyAttribute (xml , "resource-id" , node .getViewIdResourceName ());
500512 appendAttribute (xml , "class" , node .getClassName ());
501513 appendNonEmptyAttribute (xml , "package" , node .getPackageName ());
502514 appendNonEmptyAttribute (xml , "content-desc" , node .getContentDescription ());
515+ appendAttribute (xml , "visible-to-user" , Boolean .toString (node .isVisibleToUser ()));
503516 appendTrueAttribute (xml , "clickable" , node .isClickable ());
504517 appendAttribute (xml , "enabled" , Boolean .toString (node .isEnabled ()));
505518 appendTrueAttribute (xml , "focusable" , node .isFocusable ());
@@ -550,7 +563,7 @@ private static void appendNode(
550563 continue ;
551564 }
552565 try {
553- appendNode (xml , child , index , depth + 1 , maxDepth , maxNodes , stats );
566+ appendNode (xml , child , index , depth + 1 , maxDepth , maxNodes , stats , null );
554567 } finally {
555568 child .recycle ();
556569 }
@@ -571,6 +584,32 @@ private static void appendTrueAttribute(StringBuilder xml, String name, boolean
571584 }
572585 }
573586
587+ private static void appendWindowMetadata (StringBuilder xml , WindowMetadata metadata ) {
588+ appendAttribute (xml , "window-index" , Integer .toString (metadata .index ));
589+ appendAttribute (xml , "window-type" , Integer .toString (metadata .type ));
590+ appendAttribute (xml , "window-layer" , Integer .toString (metadata .layer ));
591+ appendAttribute (xml , "window-active" , Boolean .toString (metadata .active ));
592+ appendAttribute (xml , "window-focused" , Boolean .toString (metadata .focused ));
593+ appendAttribute (
594+ xml ,
595+ "window-bounds" ,
596+ String .format (
597+ Locale .ROOT ,
598+ "[%d,%d][%d,%d]" ,
599+ metadata .bounds .left ,
600+ metadata .bounds .top ,
601+ metadata .bounds .right ,
602+ metadata .bounds .bottom ));
603+ }
604+
605+ @ SuppressWarnings ("deprecation" )
606+ private static WindowMetadata readWindowMetadata (AccessibilityWindowInfo window , int index ) {
607+ Rect bounds = new Rect ();
608+ window .getBoundsInScreen (bounds );
609+ return new WindowMetadata (
610+ index , window .getType (), window .getLayer (), window .isActive (), window .isFocused (), bounds );
611+ }
612+
574613 private static void appendAttribute (StringBuilder xml , String name , CharSequence value ) {
575614 String stringValue = value == null ? "" : value .toString ();
576615 xml .append (' ' );
@@ -702,4 +741,22 @@ private static final class CaptureResult {
702741 this .truncated = truncated ;
703742 }
704743 }
744+
745+ private static final class WindowMetadata {
746+ final int index ;
747+ final int type ;
748+ final int layer ;
749+ final boolean active ;
750+ final boolean focused ;
751+ final Rect bounds ;
752+
753+ WindowMetadata (int index , int type , int layer , boolean active , boolean focused , Rect bounds ) {
754+ this .index = index ;
755+ this .type = type ;
756+ this .layer = layer ;
757+ this .active = active ;
758+ this .focused = focused ;
759+ this .bounds = bounds ;
760+ }
761+ }
705762}
0 commit comments