Skip to content

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

Merged
polmiro merged 7 commits into
next-gen-admob-sdkfrom
rewarded-interstitial-ads-pr
Aug 26, 2026
Merged

feat(admob): add next-gen rewarded interstitial ad tracking#4012
polmiro merged 7 commits into
next-gen-admob-sdkfrom
rewarded-interstitial-ads-pr

Conversation

@polmiro

@polmiro polmiro commented Aug 19, 2026

Copy link
Copy Markdown
Member

Motivation

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

Description

  • Add callback-based and suspending AdTracker.loadAndTrackRewardedInterstitialAd APIs for request-based loading through Google's Next-Gen AdRequest API.
  • Add callback-based loadAndTrackRewardedInterstitialAdFromResponse 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 RewardedInterstitialAd.show(activity, placement, onUserEarnedRewardListener) for overriding the tracking placement while forwarding Google's reward listener unchanged.
  • 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, onUserEarnedRewardListener) is called after the callback was manually replaced, the ad is still shown and the reward listener is still forwarded, but the placement override is ignored and a warning is logged.

Testing

  • ./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest
  • ./gradlew :feature:admob-next-gen:lintDefaultsDebug
  • ./gradlew :feature:admob-next-gen:metalavaCheckCompatibilityDefaultsDebug
  • ./gradlew detektAll

Note

Low Risk
Experimental public API additions following established adapter patterns; changes are isolated to ad analytics wiring with broad unit test coverage and no auth or payment logic.

Overview
Adds RevenueCat ad tracking for Google Mobile Ads Next-Gen rewarded interstitial ads, matching the patterns already used for interstitial, app open, and rewarded formats.

New AdTracker entry points cover callback and coroutine loads (loadAndTrackRewardedInterstitialAd), plus server-to-server loads via loadAndTrackRewardedInterstitialAdFromResponse with an explicit ad unit ID for attribution. Successful loads install the tracking event wrapper before app code receives the ad, while Google’s load callbacks and suspending AdLoadResult are preserved unchanged.

RewardedInterstitialAd gains a placement-aware show(activity, placement, onUserEarnedRewardListener) (reward listener forwarded as-is) and setTrackingAdEventCallback so apps can swap lifecycle callbacks without stripping RevenueCat’s wrapper. README and api.txt document usage, callback pitfalls, and list rewarded interstitial in supported formats. Unit tests cover load success/failure, suspend loads, response loads, placement override/clear, and safe callback replacement.

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

@polmiro polmiro added pr:feat A new feature pr:other and removed pr:feat A new feature labels Aug 19, 2026
@polmiro polmiro changed the title feat(admob): add next-gen rewarded interstitial tracking feat(admob): add next-gen rewarded 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
@codecov

codecov Bot commented Aug 19, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.99%. Comparing base (674d3a4) to head (0dc81c5).
⚠️ Report is 1 commits behind head on next-gen-admob-sdk.

Additional details and impacted files
@@                 Coverage Diff                 @@
##           next-gen-admob-sdk    #4012   +/-   ##
===================================================
  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.

Base automatically changed from add-next-gen-admob-event-tracking to next-gen-admob-sdk August 19, 2026 11:51
@polmiro
polmiro force-pushed the rewarded-interstitial-ads-pr branch from 08984b7 to 7c76470 Compare August 19, 2026 12:09
@emerge-tools

emerge-tools Bot commented Aug 19, 2026

Copy link
Copy Markdown

📸 Snapshot Test

621 unchanged

Name Added Removed Modified Renamed Unchanged Errored Approval
TestPurchasesUIAndroidCompatibility Paparazzi
com.revenuecat.testpurchasesuiandroidcompatibility.paparazzi
0 0 0 0 282 0 N/A
TestPurchasesUIAndroidCompatibility
com.revenuecat.testpurchasesuiandroidcompatibility
0 0 0 0 339 0 N/A

🛸 Powered by Emerge Tools

@polmiro
polmiro marked this pull request as ready for review August 19, 2026 12:26
@polmiro
polmiro requested a review from a team as a code owner August 19, 2026 12:26
@polmiro
polmiro requested review from AlvaroBrey, ajpallares, peterporfy and tonidero and removed request for ajpallares August 19, 2026 15:31
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 -->
@polmiro
polmiro force-pushed the rewarded-interstitial-ads-pr branch from efb311c to 384fb8c Compare August 20, 2026 13:40
@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Aug 20, 2026

Copy link
Copy Markdown
Public API changes in feature:admob-next-gen (6 new declarations)
+ method @KotlinOnly @com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI @kotlin.jvm.JvmSynthetic public static void loadAndTrackRewardedInterstitialAd(com.revenuecat.purchases.ads.ev…
+ method @KotlinOnly @com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackRewardedInterstitialAd(com.revenuecat.purch…
+ method @KotlinOnly @com.revenuecat.purchases.ExperimentalPreviewRevenueCatPurchasesAPI @kotlin.jvm.JvmSynthetic public static void loadAndTrackRewardedInterstitialAdFromResponse(com.revenuecat.purc…
+ public final class RewardedInterstitialAdExtensionsKt
+ 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.rewardedinterstit…

Generated by 🚫 Danger

@polmiro
polmiro force-pushed the rewarded-interstitial-ads-pr branch 2 times, most recently from 251bd72 to b75d159 Compare August 26, 2026 08:47
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 rewarded-interstitial-ads-pr branch from b75d159 to 0dc81c5 Compare August 26, 2026 09:31
@polmiro
polmiro merged commit a9ade8d into next-gen-admob-sdk Aug 26, 2026
36 checks passed
@polmiro
polmiro deleted the rewarded-interstitial-ads-pr branch August 26, 2026 09:49
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