Skip to content

feat(admob): add next-gen interstitial ad tracking - #3985

Merged
polmiro merged 16 commits into
next-gen-admob-sdkfrom
add-next-gen-admob-interstitial
Aug 26, 2026
Merged

feat(admob): add next-gen interstitial ad tracking#3985
polmiro merged 16 commits into
next-gen-admob-sdkfrom
add-next-gen-admob-interstitial

Conversation

@polmiro

@polmiro polmiro commented Aug 17, 2026

Copy link
Copy Markdown
Member

Motivation

Expose Google Mobile Ads Next-Gen interstitial loading and event tracking through a public RevenueCat API.

This PR consumes the load- and event-tracking building blocks from #3955 and #3957 and wires them into the first format-specific integration.

Description

  • Add callback-based and suspending AdTracker.loadAndTrackInterstitialAd APIs for request-based loading through Google's Next-Gen AdRequest API.
  • Add callback-based loadAndTrackInterstitialAdFromResponse for server-to-server ad responses, with an explicit ad unit ID for attribution.
  • Track successful and failed loads while preserving Google's original callbacks and suspending result.
  • Install lifecycle and paid-event tracking before a loaded ad reaches application code.
  • Add InterstitialAd.show(activity, placement) for overriding the tracking placement when the display location is only known at show time.
  • Add setTrackingAdEventCallback so applications can replace their callback without removing RevenueCat's wrapper.
  • Publish and document the experimental Kotlin API.

Tracking behavior

The load-time placement is used for load events and as the default for subsequent lifecycle events. A placement passed to show takes precedence for display, click, and revenue events.

Full-screen display is tracked from onAdShowedFullScreenContent; impressions are not also counted as displays. RevenueCat tracking runs before application callbacks.

Assigning adEventCallback directly after a tracked load replaces RevenueCat's wrapper. Applications should provide the callback while loading or use setTrackingAdEventCallback.

If show(activity, placement) is called after the callback was manually replaced, the ad is still shown but the placement override is ignored and a warning is logged.


Note

Medium Risk
New analytics surface on ad load/show paths; incorrect callback replacement or placement override could skew attribution, but changes are additive with documented guardrails.

Overview
Adds public RevenueCat tracking for Google Mobile Ads Next-Gen interstitial ads: load helpers on AdTracker, show-time placement override, and safe callback replacement without dropping the tracking wrapper.

Load paths: callback-based loadAndTrackInterstitialAd (request and server-to-server loadAndTrackInterstitialAdFromResponse with explicit adUnitId), plus a suspending overload that returns Google's unchanged AdLoadResult. Successful loads install lifecycle/paid tracking before the app callback or return; failures still emit RevenueCat load-failure events with optional placement.

Show & callbacks: InterstitialAd.show(activity, placement) overrides placement for display/click/revenue events; setTrackingAdEventCallback updates the forwarded delegate when the RC wrapper is present. README, api.txt, and tests document the experimental API and wiring.

Reviewed by Cursor Bugbot for commit e6402b9. Bugbot is set up for automated code reviews on this repo. Configure here.

@codecov

codecov Bot commented Aug 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.99%. Comparing base (22dc594) to head (eabdf12).

Additional details and impacted files
@@                 Coverage Diff                 @@
##           next-gen-admob-sdk    #3985   +/-   ##
===================================================
  Coverage               82.99%   82.99%           
===================================================
  Files                     437      437           
  Lines                   17893    17893           
  Branches                 2684     2684           
===================================================
  Hits                    14851    14851           
  Misses                   2148     2148           
  Partials                  894      894           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

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

@polmiro
polmiro force-pushed the add-next-gen-admob-event-tracking branch 2 times, most recently from 2e4006f to 2dec429 Compare August 18, 2026 13:38
@polmiro polmiro added pr:other and removed pr:feat A new feature labels Aug 18, 2026
@polmiro
polmiro force-pushed the add-next-gen-admob-event-tracking branch from 2dec429 to 2e4006f Compare August 18, 2026 15:01
@polmiro
polmiro force-pushed the add-next-gen-admob-interstitial branch from 7bc7fc7 to df7a7a7 Compare August 18, 2026 15:08
@polmiro polmiro removed the pr:other label Aug 19, 2026
@polmiro polmiro changed the title feat(admob): add next-gen interstitial extensions feat(admob): add next-gen interstitial tracking Aug 19, 2026
@polmiro polmiro added the pr:feat A new feature label Aug 19, 2026
@polmiro
polmiro marked this pull request as ready for review August 19, 2026 10:02
@polmiro
polmiro requested a review from a team as a code owner August 19, 2026 10:02
@polmiro polmiro changed the title feat(admob): add next-gen interstitial tracking feat(admob): add next-gen interstitial ad tracking Aug 19, 2026
@polmiro
polmiro force-pushed the add-next-gen-admob-event-tracking branch from b1c6581 to 138ff80 Compare August 19, 2026 11:33
An error occurred while trying to automatically change base from add-next-gen-admob-event-tracking to next-gen-admob-sdk August 19, 2026 11:51
@polmiro
polmiro force-pushed the add-next-gen-admob-interstitial branch from c0042ff to 8320171 Compare August 19, 2026 12:12
@polmiro
polmiro changed the base branch from add-next-gen-admob-event-tracking to next-gen-admob-sdk August 19, 2026 12:15
@polmiro polmiro added pr:other and removed pr:feat A new feature labels Aug 19, 2026
@polmiro
polmiro force-pushed the add-next-gen-admob-interstitial branch 2 times, most recently from bba2dc8 to f5c23a1 Compare August 19, 2026 15:55
polmiro added a commit that referenced this pull request Aug 19, 2026
…ide (#4034)

### Motivation

Two related bits of drift in the next-gen module.

**Log messages were inline literals.** The rest of the SDK keeps them in
a strings object — `purchases` has fifteen under
`com.revenuecat.purchases.strings`, and `feature/galaxy` has
`GalaxyStrings`. The two AdMob adapters are the exception, and legacy
only half follows it: `RewardVerificationStrings` holds a single
constant while six other call sites inline their text.

**The placement override is copy-pasted per format.** Every full-screen
format resolves a show-time override the same way — cast
`adEventCallback` to the tracking wrapper, assign `placement`, warn when
the cast fails. There are six copies across five branches
(`InterstitialAdExtensions.kt` #3985, `RewardedAdExtensions.kt` #4011,
`RewardedInterstitialAdExtensions.kt` #4012, `AppOpenAdExtensions.kt`
#4013, and two in `RewardVerificationExtensions.kt` #4017), and the
warning text already differs between them.

Nothing pulls those copies back together. Because they live in
**separate files**, git merges every one cleanly when those branches
land here, and the divergent wording survives silently — there is no
conflict to catch it.

Worth noting the legacy adapter already routes its four formats through
a single `applyPlacementOverride`. Next-gen dropped that shape; this
restores it rather than inventing something new, and keeps the same
name.

### Description

Two commits:

1. **`AdMobNextGenStrings`** — holds the module's three log messages
(the not-configured warning, the tracking-failure message, and the new
placement-override warning). Existing `TrackIfConfigured` call sites
route through it.
2. **`AdEventCallback?.applyPlacementOverride(placement)`** — holds the
cast condition and the warning. All six call sites collapse to one line:
   ```kotlin
   adEventCallback.applyPlacementOverride(placement)
   ```

The receiver is the shared `AdEventCallback` supertype rather than each
format's callback interface — every format callback
(`RewardedAdEventCallback`, `InterstitialAdEventCallback`,
`AppOpenAdEventCallback`, `RewardedInterstitialAdEventCallback`) extends
it, and the per-format cast was never load-bearing: an ad's
`adEventCallback` can only ever hold its own format's wrapper.

The KDoc also records that passing `null` clears the load-time
placement, which was previously documented only per-format.

### Adoption

The helper is intentionally unused here — the extensions that call it
live on the format branches, not on this base. Once this merges, each
format PR swaps its inline block for the one-liner as it rebases.
Verified that an unused `internal` function passes `detektAll` on this
branch, so this lands green on its own.

`TrackIfConfigured.kt` is byte-identical across all ten
format/preload/SSV branches, so moving its strings conflicts with none
of them.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew detektAll`
- `./scripts/api-check.sh` — no public API change; both additions are
`internal`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Internal refactor of logging and duplicated placement logic with no
public API or runtime behavior change beyond unified warning text.
> 
> **Overview**
> Introduces **`AdMobNextGenStrings`** with three shared log messages
(Purchases not configured, tracking failure, placement override ignored)
and routes **`trackIfConfigured`** through them instead of inline
literals.
> 
> Adds **`AdEventCallback?.applyPlacementOverride(placement)`** so
show-time placement override logic (cast to `TrackingAdEventCallback`,
assign placement, warn on failure) lives in one place, aligned with the
legacy AdMob adapter. The helper is **`internal`** and not wired from
format extension files on this branch; those branches are expected to
replace duplicated blocks with a single call after merge.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d9a56d2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

@AlvaroBrey AlvaroBrey left a comment

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.

These comments also apply to #4011, #4012 and #4013 in the respective places, since they are almost the same

@polmiro
polmiro requested a review from AlvaroBrey August 20, 2026 13:27
@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Aug 20, 2026

Copy link
Copy Markdown
1 Warning
⚠️ Could not read the SDK API feed channel, so this change may be announced twice: Slack rejected conversations.history: missing_scope.
Public API changes in feature:admob-next-gen (7 new declarations)
+ public final class AdTrackerAdMobExtensionsKt
+ method @KotlinOnly @com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI @kotlin.jvm.JvmSynthetic public static void loadAndTrackInterstitialAd(com.revenuecat.purchases.ads.events.AdT…
+ method @KotlinOnly @com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackInterstitialAd(com.revenuecat.purchases.ads…
+ method @KotlinOnly @com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI @kotlin.jvm.JvmSynthetic public static void loadAndTrackInterstitialAdFromResponse(com.revenuecat.purchases.ad…
+ public final class InterstitialAdExtensionsKt
+ method @KotlinOnly @com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI @kotlin.jvm.JvmSynthetic public static void setTrackingAdEventCallback(com.google.android.libraries.ads.mobile…
+ method @KotlinOnly @com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.interstitial.Inte…

Generated by 🚫 Danger

polmiro added 16 commits August 20, 2026 15:43
The flow tests asserted callback wiring and delegation but never installed a
Purchases singleton, so trackIfConfigured short-circuited on every path and the
load-time tracking arguments went unchecked. The wrapper classes are covered in
TrackingAdEventCallbackTest, but nothing pinned what the banner and interstitial
entry points hand to TrackingAdLoadCallback: swapping AdFormat.BANNER for
INTERSTITIAL, dropping the placement, or not reading adUnitId off the request all
kept the suite green.
The warning named the least likely cause. It fires whenever the ad was not
loaded through loadAndTrack, which is usually because the ad was loaded with
Google's own API rather than because someone reassigned adEventCallback, so it
sent readers looking for a reassignment that never happened.

The KDoc also only promised that the show-time placement takes precedence. It
did not say that passing null clears the placement recorded at load time, which
is easy to hit because the parameter has no default and the SDK's own show is
the only way to keep the load-time value.
The base branch now holds applyPlacementOverride, so drop this format's copy of
the cast, the assignment and the warning in favour of the shared one. That leaves
a single definition of the message rather than one per format.
@polmiro
polmiro force-pushed the add-next-gen-admob-interstitial branch from d26145a to eabdf12 Compare August 20, 2026 13:45
@polmiro
polmiro merged commit 40b51c5 into next-gen-admob-sdk Aug 26, 2026
36 checks passed
@polmiro
polmiro deleted the add-next-gen-admob-interstitial branch August 26, 2026 08:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants