Skip to content

PANA-5681: Integrate HeatmapIdentifierRegistry into RUM for heatmaps [3 of 4]#3205

Open
gonzalezreal wants to merge 1 commit into
feature/heatmapsfrom
gonzalezreal/PANA-5681/rum-heatmaps
Open

PANA-5681: Integrate HeatmapIdentifierRegistry into RUM for heatmaps [3 of 4]#3205
gonzalezreal wants to merge 1 commit into
feature/heatmapsfrom
gonzalezreal/PANA-5681/rum-heatmaps

Conversation

@gonzalezreal
Copy link
Copy Markdown

@gonzalezreal gonzalezreal commented Feb 27, 2026

What does this PR do?

Integrates heatmap tap data into the RUM feature. When a tap gesture is detected and the view is attached to a window, GesturesListener captures the touch coordinates, the view's dimensions, and (when available) its heatmap identifier from the HeatmapIdentifierRegistry, and packages them into a new HeatmapActionData object. This data flows through AdvancedRumMonitor.addActionWithHeatmap — an internal API that keeps HeatmapActionData off the public RumMonitor interface — into RumActionScope, where it is serialised into the dd.action field of the outgoing ActionEvent. RumFeature now implements HeatmapIdentifierRegistryProvider, exposing its lazily-initialised registry to Session Replay via FeatureScope.unwrap() without going through the untyped feature-context map.

Motivation

Support mobile heatmaps. This is PR 3 of 4, integrates the HeatmapIdentifierRegistry (#3202) and updated schemas (#3203) into RUM.

Additional Notes

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)

Copy link
Copy Markdown

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

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: 087cbc56d0

ℹ️ 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".

@gonzalezreal gonzalezreal force-pushed the gonzalezreal/PANA-5681/rum-heatmaps branch from 7c039dc to e76417d Compare February 27, 2026 13:17
@datadog-datadog-prod-us1-2

This comment has been minimized.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Feb 27, 2026

Codecov Report

❌ Patch coverage is 95.69892% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 72.10%. Comparing base (81e4f04) to head (e63b7f4).
⚠️ Report is 2 commits behind head on feature/heatmaps.

Files with missing lines Patch % Lines
...instrumentation/gestures/DatadogGesturesTracker.kt 50.00% 2 Missing ⚠️
...ernal/instrumentation/gestures/GesturesListener.kt 94.44% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@                 Coverage Diff                  @@
##           feature/heatmaps    #3205      +/-   ##
====================================================
+ Coverage             72.09%   72.10%   +0.01%     
====================================================
  Files                   964      965       +1     
  Lines                 35429    35492      +63     
  Branches               5880     5888       +8     
====================================================
+ Hits                  25542    25591      +49     
- Misses                 8278     8286       +8     
- Partials               1609     1615       +6     
Files with missing lines Coverage Δ
...oid/internal/heatmaps/HeatmapIdentifierRegistry.kt 100.00% <ø> (ø)
...ndroid/internal/heatmaps/HeatmapIdentifierStore.kt 88.89% <100.00%> (ø)
...lin/com/datadog/android/rum/internal/RumFeature.kt 92.86% <100.00%> (+0.30%) ⬆️
...oid/rum/internal/domain/scope/HeatmapActionData.kt 100.00% <100.00%> (ø)
...ndroid/rum/internal/domain/scope/RumActionScope.kt 97.73% <100.00%> (+0.12%) ⬆️
...g/android/rum/internal/domain/scope/RumRawEvent.kt 100.00% <100.00%> (ø)
...droid/rum/internal/domain/scope/RumSessionScope.kt 91.03% <100.00%> (ø)
.../android/rum/internal/monitor/DatadogRumMonitor.kt 86.92% <100.00%> (+0.61%) ⬆️
...instrumentation/gestures/DatadogGesturesTracker.kt 63.04% <50.00%> (-0.59%) ⬇️
...ernal/instrumentation/gestures/GesturesListener.kt 92.81% <94.44%> (+0.30%) ⬆️

... and 33 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 changed the title PANA-5681: Integrate ViewIdentityResolver into RUM for heatmaps PANA-5681: Integrate ViewIdentityResolver into RUM for heatmaps [3 of 4] May 5, 2026
@jonathanmos jonathanmos marked this pull request as draft May 5, 2026 10:51
@jonathanmos jonathanmos force-pushed the gonzalezreal/PANA-5681/schema-updates branch from ec4040e to 81e4f04 Compare May 13, 2026 08:29
Base automatically changed from gonzalezreal/PANA-5681/schema-updates to feature/heatmaps May 13, 2026 11:51
@jonathanmos jonathanmos force-pushed the gonzalezreal/PANA-5681/rum-heatmaps branch 2 times, most recently from cbfbf76 to 9a2e330 Compare May 14, 2026 12:23
@jonathanmos jonathanmos changed the title PANA-5681: Integrate ViewIdentityResolver into RUM for heatmaps [3 of 4] PANA-5681: Integrate HeatmapIdentifierRegistry into RUM for heatmaps [3 of 4] May 14, 2026
@jonathanmos jonathanmos force-pushed the gonzalezreal/PANA-5681/rum-heatmaps branch from 9a2e330 to e63b7f4 Compare May 18, 2026 08:12
@jonathanmos jonathanmos marked this pull request as ready for review May 18, 2026 09:07
@jonathanmos
Copy link
Copy Markdown
Member

@codex review

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Breezy!

ℹ️ 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 +344 to +345
val featureSdkCore = sdkCore as? FeatureSdkCore ?: return null
return featureSdkCore
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should always be able to perform this cast, so can drop the nullable:

Suggested change
val featureSdkCore = sdkCore as? FeatureSdkCore ?: return null
return featureSdkCore
return (sdkCore as FeatureSdkCore)

* Replaces the current snapshot with [identifiers], scoped to [screenName].
*
* @param identifiers a map of [android.view.View.getId] values to their [HeatmapIdentifier]s,
* @param identifiers a map of [System.identityHashCode] values to their [HeatmapIdentifier]s,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think @aleksandr-gringauz had a point regarding identityHashCode usage (that there is a clash after certain number of objects, since it is re-used), but since we use it in the Session Replay for the object identity, I guess this is fine.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

yes, that is actually the reason I chose to use it here. We don't expect collisions on the same screen, but if we reach a conclusion that there is an issue we could append the hashcode of the direct parent for an even lower collision probability

Copy link
Copy Markdown
Contributor

@aleksandr-gringauz aleksandr-gringauz May 26, 2026

Choose a reason for hiding this comment

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

Yes. See this thread.

We don't expect collisions on the same screen

I didn't dig deep into how the identifier obtained using identityHashCode is used in this PR.

However in general, it is important to understand the following.

2 instances of the same type that currently have strong references in code can have the same identityHashCode. And the probability of this is higher than you might expect. This is what I explain in my comment.

The questions are:

  1. What are the requirements here for not having collisions? Do we have to avoid collisions during the lifetime of a single RUM View?
  2. And what is the number of "tries"? Is it the number of View instances that ever existed during the lifetime of the RUM View?

internal var displayInfoProvider: InfoProvider<DisplayInfo> = NoOpDisplayInfoProvider()
internal val rumContextUpdateReceivers = mutableSetOf<FeatureContextUpdateReceiver>()
internal var insightsCollector: InsightsCollector = NoOpInsightsCollector()
override val heatmapIdentifierRegistry: HeatmapIdentifierRegistry by lazy { HeatmapIdentifierRegistry.create() }
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

what is the need for the lazy? why we cannot simply provide it in the constructor or initialize directly?

),
configuration = ActionEvent.Configuration(sessionSampleRate = sampleRate)
configuration = ActionEvent.Configuration(sessionSampleRate = sampleRate),
action = ddAction
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: since heatmapData is immutable reference and HeatmapActionData is a data class, we can simply drop ddAction (name is a bit confusing) and do object creations here directly:

Suggested change
action = ddAction
action = heatmapData?.let { ActionEvent.DdAction(...) }


if (view.isAttachedToWindow) {
resolveHeatmapIdentifier(view)?.let { identity ->
val locationInWindow = IntArray(2)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

minor: since we expect all the UI events to happen on the main thread, can we just allocate static array and re-use it for the calls? instead of allocating a new one for each call.

although probably not a big difference since it is for the taps, they don't happen that often

Comment on lines +36 to +37
attributes: Map<String, Any?>,
heatmapData: HeatmapActionData?
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: generic attributes argument should come last

val attributes: Map<String, Any?>,
override val eventTime: Time = Time()
override val eventTime: Time = Time(),
val heatmapData: HeatmapActionData? = null
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: maybe put it before generic attributes and eventTime in the arguments list?

verify(rumMonitor.mockInstance as AdvancedRumMonitor).addActionWithHeatmap(
eq(RumActionType.TAP),
eq(""),
argThat { attributes ->
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

argThat gives just a generic description when verify fails. It is better to use argumentCaptor here and assert the argument independently.

any(),
any()
)
verify(rumMonitor.mockInstance as AdvancedRumMonitor, never()).addActionWithHeatmap(
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

will verifyNoMoreInteractions(rumMonitor.mockInstance) work here?

verify(rumMonitor.mockInstance as AdvancedRumMonitor).addActionWithHeatmap(
eq(RumActionType.TAP),
eq(""),
argThat { attributes ->
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

it is better to use argument captor, same for the similar tests below

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants