Skip to content

PANA-8613: View traversal and decomposition - #3728

Open
jonathanmos wants to merge 10 commits into
feature/sr-new-pipelinefrom
jmoskovich/03-sr-new-pipeline
Open

jonathanmos wants to merge 10 commits into
feature/sr-new-pipelinefrom
jmoskovich/03-sr-new-pipeline

Conversation

@jonathanmos

@jonathanmos jonathanmos commented Aug 19, 2026 •

Copy link
Copy Markdown
Member

What does this PR do?

This is the third PR in the new composition-based Session Replay pipeline. It adds native View traversal and snapshot production to the new pipeline, covering text, WebView placeholders, hidden-view placeholders, and background shapes.

Capture can pause and resume within a window while respecting time budgets and cancellation. It preserves drawing order and clipping, avoids copying ancestor bounds at every level, and prevents cancelled captures from carrying partial touch-privacy overrides into later captures.

Motivation

Build on the capture orchestration introduced in the previous PR by producing real snapshots from the View hierarchy.

Additional Notes

  • Hidden-view handling and touch privacy are included here; text/input masking follows later in the stack.
  • Capture supports attached, main-thread-owned windows. Windows owned by other UI threads are excluded.
  • WebViews produce a single placeholder without traversing their native children. An optional Compose handoff is provided; full Compose and embedded-content support follow later.

Review checklist (to be filled by reviewers)

  • Feature or bugfix MUST have appropriate tests (unit, integration, e2e)
  • Make sure you discussed the feature or bugfix with the maintaining team in an Issue
  • Make sure each commit and the PR mention the Issue number (cf the CONTRIBUTING doc)

@datadog-official

datadog-official Bot commented Aug 19, 2026 •

Copy link
Copy Markdown

Tests

✅ All CI checks and tests passed. Datadog automation helped this PR pass.

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

🔄 Datadog retried 1 test - 1 passed on retry View in Datadog

🎯 Code Coverage (details)
• Patch Coverage: 88.21%
• Overall Coverage: 72.51% (+0.28%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: b4e4f19 | Docs | View more details | Give us feedback!

@codecov-commenter

codecov-commenter commented Aug 19, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 77.62040% with 79 lines in your changes missing coverage. Please review.
✅ Project coverage is 73.39%. Comparing base (50400d0) to head (5ce8684).

Files with missing lines Patch % Lines
...ernal/composition/mapper/CapturedTextViewMapper.kt 40.96% 44 Missing and 5 partials ⚠️
...lay/internal/composition/AndroidWindowTraversal.kt 88.79% 2 Missing and 10 partials ⚠️
...nal/composition/AndroidCapturedSnapshotProducer.kt 83.33% 3 Missing and 4 partials ⚠️
...eplay/internal/composition/RumViewScopeProvider.kt 50.00% 5 Missing ⚠️
.../sessionreplay/internal/DefaultRecorderProvider.kt 92.86% 2 Missing and 1 partial ⚠️
...l/composition/mapper/CapturedViewMapperRegistry.kt 87.50% 1 Missing and 1 partial ⚠️
...ion/mapper/CapturedBackgroundShapeStyleResolver.kt 85.71% 0 Missing and 1 partial ⚠️
Additional details and impacted files
@@                        Coverage Diff                        @@
##           jmoskovich/02-sr-new-pipeline    #3728      +/-   ##
=================================================================
+ Coverage                          73.35%   73.39%   +0.05%     
=================================================================
  Files                               1021     1032      +11     
  Lines                              37902    38221     +319     
  Branches                            6599     6659      +60     
=================================================================
+ Hits                               27800    28052     +252     
- Misses                              8183     8227      +44     
- Partials                            1919     1942      +23     
Files with missing lines Coverage Δ
...sessionreplay/internal/composition/CapturedTree.kt 100.00% <100.00%> (+9.76%) ⬆️
...nal/composition/mapper/CapturedHiddenViewMapper.kt 100.00% <100.00%> (ø)
...al/composition/mapper/CapturedMapperTypeWrapper.kt 100.00% <100.00%> (ø)
...position/mapper/CapturedViewGroupFallbackMapper.kt 100.00% <100.00%> (ø)
.../internal/composition/mapper/CapturedViewMapper.kt 100.00% <100.00%> (ø)
...ternal/composition/mapper/CapturedWebViewMapper.kt 100.00% <100.00%> (ø)
...ion/mapper/CapturedBackgroundShapeStyleResolver.kt 85.71% <85.71%> (ø)
...l/composition/mapper/CapturedViewMapperRegistry.kt 87.50% <87.50%> (ø)
.../sessionreplay/internal/DefaultRecorderProvider.kt 92.59% <92.86%> (+0.59%) ⬆️
...eplay/internal/composition/RumViewScopeProvider.kt 50.00% <50.00%> (ø)
... and 3 more

... and 37 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@jonathanmos
jonathanmos force-pushed the jmoskovich/02-sr-new-pipeline branch from 1dd5cfc to 50400d0 Compare August 19, 2026 10:38
@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch from 92ae883 to 5ce8684 Compare August 19, 2026 10:46
@jonathanmos
jonathanmos force-pushed the jmoskovich/02-sr-new-pipeline branch from 50400d0 to 35e15c5 Compare August 27, 2026 12:34
@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch 2 times, most recently from 6e5525f to e67102f Compare August 27, 2026 14:23
@jonathanmos
jonathanmos force-pushed the jmoskovich/02-sr-new-pipeline branch 4 times, most recently from 06dc1f8 to 783469c Compare September 2, 2026 13:59
@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch 2 times, most recently from 0168738 to a4083d3 Compare September 6, 2026 18:21
@jonathanmos
jonathanmos force-pushed the jmoskovich/02-sr-new-pipeline branch from 783469c to 902b1be Compare September 6, 2026 20:44
@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch from a4083d3 to 5287820 Compare September 7, 2026 08:48
@jonathanmos
jonathanmos force-pushed the jmoskovich/02-sr-new-pipeline branch 2 times, most recently from a21f246 to 357bf07 Compare September 7, 2026 11:57
@jonathanmos
jonathanmos force-pushed the jmoskovich/02-sr-new-pipeline branch 4 times, most recently from 294da51 to fd9fa08 Compare September 24, 2026 12:45
@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch from 5287820 to 5b481bc Compare September 24, 2026 13:07
@jonathanmos
jonathanmos force-pushed the jmoskovich/02-sr-new-pipeline branch from bd69260 to 751e07a Compare September 29, 2026 07:10
Base automatically changed from jmoskovich/02-sr-new-pipeline to feature/sr-new-pipeline September 30, 2026 11:56
@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch from 883ec03 to b94ea39 Compare October 6, 2026 10:01
@jonathanmos

Copy link
Copy Markdown
Member Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 6, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-13T14:47:28.588826Z b4e4f19 New commits
🔒 Security Review ✅ Completed 2026-10-13T14:43:21.717502Z b4e4f19 New commits

Security findings

Advisory findings (1)

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b94ea39e26

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛡️ Codex Security Review · Automatically triggered

Here are some automated security review suggestions for this pull request.

Reviewed commit: b94ea39e26

ℹ️ About Codex security reviews in GitHub

This is an experimental Codex feature. Security reviews are triggered when:

  • You comment "@codex security review"
  • A regular code review gets triggered (for example, "@codex review" or when a PR is opened), and you’re opted in so security review runs alongside code review

Once complete, Codex will leave suggestions, or a comment if no findings are found.

Collect touch overrides in each capture and publish them only after all
windows complete. Cancelled or aborted captures discard their partial
overrides without contaminating a later generation.

Cover queued cancellation, aborted traversal, and atomic publication.

Validated composition and touch-privacy unit tests and ktlint.
Recheck the recording time bank before every resumed slice and start
the slice clock when execution begins. Queue time continues to count
toward the generation deadline without consuming a slice allowance.

Cover denied resumes, budget recovery, and delayed continuations.

Validated composition and touch-privacy unit tests and ktlint.
Use effective child drawing order for traversal and occlusion, including
custom order and elevation. Respect child and padding clipping and
exclude restrictively clipped siblings from full-occlusion culling.

Carry accumulated clip intersections to avoid copying ancestor lists.
Add drawing-order, clipping, occlusion, and deep-hierarchy regressions.

Validated composition and touch-privacy unit tests and ktlint.
Mark WebView mapping results as terminal so traversal emits the WebView
slot without separately capturing its implementation children.

Cover native descendants, hidden WebViews, and ordinary containers.

Validated composition and touch-privacy unit tests and ktlint.
Filter window interception and capture by the attached root handler.
Defer detached roots and abort a partial capture if ownership is lost
before visiting or resuming a window. Remove draw listeners through
their original observer.

Secondary-thread windows are explicitly outside composition capture.
Cover ownership filtering, attachment changes, and observer cleanup.

Validated full debug/release unit suites, ktlint, Detekt, release
lint, and API surface checks.
@jonathanmos
jonathanmos marked this pull request as ready for review October 7, 2026 09:52
@jonathanmos
jonathanmos requested review from a team as code owners October 7, 2026 09:52

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e5db3ff2fc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

windowLayers += result.rootLayer
layers += result.layers
wireframes += result.wireframes
touchOverrideAreas += result.touchOverrideAreas

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Scope touch overrides to the dispatching window

When windows overlap, merging every window's override map lets an obscured lower window control privacy for touches dispatched to the top window. For example, with global HIDE, an activity containing a SHOW region beneath an untagged modal dialog causes touches on the dialog to be recorded because every CompositionWindowTouchCallback consults this shared merged map. Keep overrides window-specific or exclude lower-window regions covered by higher windows.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid issue. will address per-window touch privacy isolation in a follow-up PR before release to keep this PR focused on view traversal and decomposition.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 51d12d5d5c

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 623d5e7400

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +330 to +334
val viewArea = Rect(
x,
y,
x + view.width,
y + view.height

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Clip touch overrides to the effective visible region

When a SHOW-tagged child extends outside a clipping parent, this raw view rectangle includes pixels where the child cannot be drawn or receive the touch. With global touch privacy set to HIDE, a touch handled by another view in that clipped-out area is therefore recorded because it still falls inside this override. Intersect the override with ancestor, padding, and explicit clip constraints before publishing it.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be addressed in a followup pr

@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch from 623d5e7 to 85a072b Compare October 7, 2026 14:56

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 85a072bd65

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@maciejburda
maciejburda requested a review from satween October 8, 2026 07:06
@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch from 85a072b to 561fb40 Compare October 8, 2026 08:13

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 561fb40a1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch from 561fb40 to 96247a0 Compare October 8, 2026 09:53

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 96247a0233

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@jonathanmos
jonathanmos force-pushed the jmoskovich/03-sr-new-pipeline branch from 96247a0 to a27f444 Compare October 8, 2026 10:37

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a27f444b06

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +180 to +182
val inheritedClip = if (item.clipToBounds) item.ancestorClip.intersectWith(bounds) else item.ancestorClip
val clip = view.clipBoundsOnScreen(state.screenDensity)?.let { inheritedClip.intersectWith(it) }
?: inheritedClip

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply clipToOutline to the captured subtree

When a view uses clipToOutline = true, such as a rounded card or an oval container, Android clips the view and its descendants to the outline, but this path only incorporates rectangular ancestor and clipBounds constraints. The replay therefore renders child content and backgrounds into corners or regions that are absent on screen. Resolve the view outline into a layer clip modifier, or otherwise fall back when the outline cannot be represented.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be addressed in a followup pr

Comment on lines +196 to +198
val orderedWindows = orderedDecorViews.mapNotNull { root ->
trackedByDecorView[root] ?: windowFromDecorView(root)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Intercept touches for unresolved untracked roots

When an untracked root cannot be converted to a Window—notably a PopupWindow root, or a dialog decor when private-field reflection is blocked—it remains in decorViews and is captured visually, but mapNotNull silently omits it from windows. No CompositionWindowTouchCallback is then installed for that root, so every interaction inside the displayed popup or dialog is absent from the replay. Provide a root-level touch fallback for unresolved roots rather than dropping them only from touch interception.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will be addressed in a followup pr

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4e4f19040

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 109 to 110
interceptor.intercept(resolved.decorViews)
touchInterceptor.intercept(resolved.windows)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Delay touch interception until privacy areas are ready

When recording starts or an attached window is newly discovered with global touch privacy set to SHOW, this installs its touch callback immediately after merely requesting a delayed capture. The override map is still empty until the 64 ms-delayed traversal finishes, so touches on views tagged HIDE during that interval are recorded using the global SHOW policy. Delay enabling the touch callback for a window until its first complete privacy map is published, or otherwise keep touch recording fail-closed during initialization.

Useful? React with 👍 / 👎.


collectTouchOverrideArea(view, state)

val bounds = viewBoundsResolver.resolveViewGlobalBounds(view, state.screenDensity).toCaptured()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve scale and rotation in captured layers

For any view with non-default scaleX, scaleY, or rotation—such as during a common property animation—the bounds resolver still returns the untransformed width and height, and the resulting layer records only an opacity modifier. Its text, background, and subtree are therefore replayed as an unscaled, unrotated rectangle even though Android renders a transformed view. Encode the spatial transform on the layer or capture the transformed output instead.

Useful? React with 👍 / 👎.

return if (textView.text.isNullOrEmpty()) {
resolveHintTextColor(textView)
} else {
colorStringFormatter.formatColorAndAlphaAsHexString(textView.currentTextColor, OPAQUE_ALPHA_VALUE)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve alpha from the current text color

When a nonempty TextView uses a translucent current color—commonly a disabled or secondary-text color from a state list—passing OPAQUE_ALPHA_VALUE replaces that color's alpha with 0xff. The layer opacity only represents View.alpha, so it cannot restore this per-color transparency and the replay renders the text fully opaque. Serialize the alpha already present in currentTextColor instead.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants