Skip to content

feat(admob): add next-gen SDK support - #3958

Open
polmiro wants to merge 42 commits into
mainfrom
next-gen-admob-sdk
Open

feat(admob): add next-gen SDK support#3958
polmiro wants to merge 42 commits into
mainfrom
next-gen-admob-sdk

Conversation

@polmiro

@polmiro polmiro commented Aug 13, 2026

Copy link
Copy Markdown
Member

Checklist

  • Unit tests
  • Follow-up issues for purchases-ios and hybrids are not applicable because this adapter targets the Android-only Google Mobile Ads SDK

Motivation

The existing purchases-admob adapter targets the legacy Google Mobile Ads SDK and cannot be used with Google’s Next-Gen SDK.

This PR introduces a separate adapter for applications migrating to Google Mobile Ads Next-Gen while preserving RevenueCat ad-event tracking across supported formats.

Description

This PR adds the publishable purchases-admob-next-gen artifact and its complete tracking integration.

All changes in this integration PR were reviewed and approved in their individual PRs before being integrated into this branch.

Adapter support

  • Track loaded, failed-to-load, displayed, opened, and revenue events.
  • Support banner, interstitial, app-open, rewarded, rewarded-interstitial, and native ads.
  • Provide callback-based and suspending direct-loading APIs.
  • Support server-to-server ad responses where available.
  • Support Google’s preloaders for every supported format, preserving separate preload, poll, and show placements.
  • Support single and batched native-ad loading.
  • Support RevenueCat reward verification for rewarded and rewarded-interstitial ads.
  • Preserve and forward application callbacks after RevenueCat tracking.
  • Isolate tracking failures so they do not interfere with Google Mobile Ads callbacks.

Distribution and compatibility

  • Publish com.revenuecat.purchases:purchases-admob-next-gen.
  • Add the new artifact to the RevenueCat BOM.
  • Depend on Google Mobile Ads Next-Gen SDK 1.3.0.
  • Require Android API 24+, compile SDK 35+, and Kotlin 1.9+.
  • Keep purchases-admob-next-gen mutually exclusive with the legacy purchases-admob adapter and legacy Google Mobile Ads artifacts.

Documentation and sample

  • Document installation, initialization, supported APIs, event semantics, placements, callback handling, and preloading.
  • Add a migration guide from the legacy RevenueCat AdMob adapter.
  • Add a runnable Compose sample covering direct loading, preloading, placement overrides, reward verification, native batch loading, and failure diagnostics.

CI and release integration

  • Run adapter unit tests through the repository’s aggregate debug and release test tasks.
  • Include the module in build, Detekt, lint, Metalava, publishing, and BOM validation.
  • Add a dedicated Fastlane lane and CircleCI job that build the Next-Gen sample.

Validation

  • Debug and release unit-test suites
  • Android lint
  • Detekt
  • Metalava API compatibility
  • Next-Gen sample assembly through Fastlane and CircleCI

Note

Medium Risk
New published SDK surface and ad analytics plumbing tied to Google’s Next-Gen APIs; mistakes could mis-attribute events or break when mixed with the legacy AdMob stack, but core purchase/auth paths are untouched.

Overview
Introduces purchases-admob-next-gen, a new RevenueCat adapter for Google’s Mobile Ads Next-Gen SDK (alongside the legacy purchases-admob artifact, which apps must not combine with it). The module wires RevenueCat ad-event tracking into Next-Gen load, show, preload, native batch Flow, server-to-server responses, placement overrides, and reward verification, with callback wrappers that forward app code after tracking.

Distribution and validation updates include adding the artifact to the BOM, a Compose sample (admob-next-gen-sample) that exercises every format, a large adapter README with migration guidance, and CircleCI/Fastlane jobs to assemble the new sample.

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

@polmiro polmiro added the pr:feat A new feature label Aug 13, 2026
@polmiro polmiro closed this Aug 13, 2026
@polmiro
polmiro force-pushed the next-gen-admob-sdk branch from da804a9 to 826c49f Compare August 13, 2026 08:51
@polmiro polmiro reopened this Aug 13, 2026
@codecov

codecov Bot commented Aug 13, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 83.55%. Comparing base (9da1fc7) to head (1552ef5).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3958   +/-   ##
=======================================
  Coverage   83.55%   83.55%           
=======================================
  Files         454      454           
  Lines       18225    18225           
  Branches     2750     2750           
=======================================
  Hits        15228    15228           
  Misses       2107     2107           
  Partials      890      890           

☔ 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 next-gen-admob-sdk branch from f72c7ab to a541d70 Compare August 17, 2026 08:33
### Checklist

- [x] Unit tests are not applicable because this PR contains no
functional code
- [x] Follow-up issues for `purchases-ios` and hybrids are not
applicable to this Android-specific module

### Motivation

Prepare the Android SDK repository for Google Mobile Ads Next-Gen
support by introducing the publishable module that subsequent changes
will build on.

### Description

- Add the public `feature/admob-next-gen` Android library with minimum
SDK 24, Metalava configuration, and Maven metadata.
- Pin Google Mobile Ads Next-Gen SDK 1.3.0 in the version catalog.
- Allow the SDK's Cronet dependencies to resolve from Google Maven.
- Register the new artifact in Gradle settings and the RevenueCat BOM.
- Seed the empty API signature, consumer rules, and package structure
for follow-up implementation work.
- Add initial setup documentation covering requirements, dependency and
BOM configuration, legacy SDK exclusions, adapter mutual exclusivity,
and Next-Gen SDK initialization.

No functional SDK code is included in this PR. Format-specific usage and
migration guidance will land alongside the relevant follow-up
implementation PRs.

### Validation

- `assembleDefaultsRelease`
- Metalava compatibility check for the release variant
- Module lint
- BOM build and Maven POM generation
- Dependency graph inspection
- Custom-entitlement release configuration check
- README link and whitespace checks

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Scaffolding-only: Gradle, BOM, and documentation with no runtime or
public API behavior changes.
> 
> **Overview**
> Introduces **`feature/admob-next-gen`** as a public Android library
(`purchases-admob-next-gen`, **minSdk 24**) wired like the legacy AdMob
feature: Metalava **`api.txt`**, Maven POM metadata, and dependencies on
**`:purchases`** plus **`ads-mobile-sdk` 1.3.0** from the version
catalog.
> 
> **Build integration:** registers the module in
**`settings.gradle.kts`**, adds a BOM constraint alongside
**`feature:admob`**, and extends Google Maven content filtering with
**`org.chromium.net`** so Next-Gen/Cronet artifacts resolve.
> 
> **Docs:** README covers mutual exclusivity with **`purchases-admob`**,
install/BOM snippets, legacy artifact excludes, and Next-Gen
**`MobileAds.initialize`** guidance. The API surface is intentionally
empty (placeholder signature only)—no functional adapter code in this
PR.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
66e1661. 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 next-gen-admob-sdk branch from f32d4ae to 809d236 Compare August 18, 2026 08:33
### Motivation

Create the shared internal runtime needed by both load tracking and
post-load event tracking before those implementations split into sibling
pull requests.

### Description

- Add a failure-isolated guard that skips tracking until `Purchases` is
configured.
- Centralize adapter capture stamping for loaded, failed, displayed,
opened, and revenue events.
- Add shared callback contract assertions for the load and event
tracking layers.
- Add focused tests for configuration handling, failure isolation, and
every adapter capture overload.
- Remove the obsolete source placeholder now that the module contains
implementation code.

This PR intentionally contains no load or event callback wrappers,
format-specific behavior, public API, or usage documentation.

### Validation

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

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Internal-only foundation with no public API or live ad callbacks;
changes are isolated to the new module and well covered by unit tests.
> 
> **Overview**
> Adds **internal** building blocks for AdMob next-gen ad event tracking
ahead of load and post-load callback wrappers in follow-up PRs.
> 
> **`trackIfConfigured`** gates all tracking on
`Purchases.isConfigured`, warns when skipped, and wraps work in
`runCatching` so RevenueCat tracking failures never propagate out of
Google Mobile Ads callbacks.
> 
> **`AdTracker.trackFromAdapter`** overloads route loaded,
failed-to-load, displayed, opened, and revenue events through the public
tracker with **`AdCaptureMethod.ADAPTER`** so adapter-captured events
differ from manual developer calls.
> 
> A small **`Logger`** (`PurchasesAdMob` tag) backs those paths.
**`assertOverridesAllSdkCallbacks`** (reflection) will enforce that
future tracking callbacks cover every public SDK callback method.
> 
> Gradle enables unit test default return values and
**`libs.bundles.test`**; tests cover configuration gating, failure
isolation, adapter capture stamping, and the callback contract helper.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5ef64dc. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Motivation

Add the load-lifecycle layer for RevenueCat ad tracking in the Google
Mobile Ads Next-Gen integration.

This PR builds on the shared tracking runtime in #3990 and intentionally
excludes display, impression, click, revenue, and public format loading
APIs.

### Description

- Wrap generic Google Mobile Ads load callbacks and report successful
and failed loads through RevenueCat’s adapter capture path.
- Wrap banner refresh callbacks and read response information when the
refresh fires so refreshed creatives use their current network and
response ID.
- Wrap the native loader’s distinct callback contract, tracking standard
and custom-native fills as native, banner fills as banner, and
forwarding completion callbacks unchanged.
- Report a single native failure event for native-loader failures
because the SDK reports one failed request, not a failure for every
candidate format.
- Provide configuration hooks that run after successful load tracking
and before application delegation. The native-loader hook applies only
to standard `NativeAd` results; custom-native and banner fills are
tracked and delegated without using that hook.
- Map load metadata including network, response ID, placement, ad unit,
format, and numeric SDK error values.
- Preserve application behavior when RevenueCat is unconfigured or
tracking fails; failed loads are never configured.
- Verify that every wrapped Google callback is explicitly overridden and
forwarded.

### Stack

- Based on #3990.
- Targets the `agent/add-next-gen-admob-tracking-foundation` branch
until the foundation merges into `next-gen-admob-sdk`.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew :feature:admob-next-gen:lintDefaultsDebug detektAll`
- `./scripts/api-check.sh`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Internal adapter instrumentation with defensive error handling; no
changes to core Purchases or public ad APIs beyond documented next-gen
module behavior.
> 
> **Overview**
> Adds the **load-lifecycle** layer for the AdMob Next-Gen adapter:
callback wrappers that emit RevenueCat **Ad Loaded** and **Ad Failed to
Load** events (with `AdCaptureMethod.ADAPTER`) before forwarding every
Google Mobile Ads callback to the app.
> 
> **Generic loads** use `TrackingAdLoadCallback`, which tracks
success/failure, runs an optional `configureAd` hook only on successful
loads (track → configure → delegate), and maps network, response ID,
placement, ad unit, format, and numeric SDK error codes.
> 
> **Banner auto-refresh** uses `TrackingBannerAdRefreshCallback`,
treating refresh success/failure as load events and reading
`ResponseInfo` when the refresh callback fires so refreshed creatives
get the current network and impression id.
> 
> **Native loader** uses `TrackingNativeAdLoaderCallback` for the
multi-callback contract: standard/custom native tracked as native,
banner fills as banner; `configureAd` applies only to standard
`NativeAd`; `onAdLoadingCompleted` is forwarded without tracking.
> 
> Shared helpers include `trackIfConfigured` (skip or swallow errors so
app callbacks always run), `AdapterTracking` extensions, and internal
`Logger`. README documents tracked events and forwarding behavior. Unit
tests cover ordering, metadata, unconfigured Purchases, and
reflection-based SDK callback contract coverage.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7951730. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Motivation

Complete the ad event lifecycle for RevenueCat tracking in the Google
Mobile Ads Next-Gen integration.

This PR is stacked on the load-tracking PR and intentionally focuses on
events emitted after an ad has loaded. Nothing wires these callbacks
into a public entry point yet, so everything added here is `internal`
and reachable only from tests; wiring lands in the follow-up PR in this
stack.

### Description

- Track banner and native displays from impression callbacks.
- Track fullscreen displays from the fullscreen-show callback.
- Track clicks as opened events.
- Track paid callbacks as revenue events with network, response,
placement, currency, and micros data.
- Map every Google Mobile Ads precision value to the corresponding
RevenueCat precision, falling back to `UNKNOWN` for constants introduced
by an SDK newer than the one this module is compiled against, so a
precompiled artifact cannot throw inside the paid callback.
- Read response info at event time rather than capturing it at load, so
auto-refreshing banners report the currently displayed creative instead
of the first-loaded one.
- Preserve all common and format-specific application callbacks by
delegating after tracking.
- Verify in tests that every callback each SDK interface exposes is
overridden and forwarded to the application delegate unchanged, and that
swapping the delegate or the placement is reflected at event time.
- Declare per format the event each SDK callback must produce, and
assert that declaration matches the SDK's callback surface exactly, so a
stray tracking call cannot ship and a callback added by a newer SDK
cannot slip through untracked.

Display tracking uses one explicit callback per format and does not keep
deduplication state.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew :feature:admob-next-gen:lintDefaultsDebug`
- `./gradlew detektAll`
- `./scripts/api-check.sh`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes ad analytics and revenue attribution semantics (display
deduplication per format, event-time response info) across all formats;
internal-only until wired, but incorrect mapping would skew metrics.
> 
> **Overview**
> Adds **internal** post-load RevenueCat ad tracking for the Google
Mobile Ads Next-Gen adapter (display, opened, revenue), still not wired
to public APIs in this PR.
> 
> **`TrackingAdEventCallback`** and per-format wrappers track **Ad
Displayed** from `onAdImpression` (banner/native) or
`onAdShowedFullScreenContent` (full-screen formats), **Ad Opened** from
clicks, and **Ad Revenue** from `onAdPaid`, with mediation network and
impression id read via a **`responseInfoProvider` at event time** so
refreshing banners stay tied to the on-screen creative. Placement is
also read at event time on event callbacks; banner refresh tracking
keeps placement fixed at load.
> 
> **`AdMobPrecisionMapping`** maps GMA `PrecisionType` to RevenueCat
precision, with a non-exhaustive `else` → `UNKNOWN` so newer SDK
constants on a precompiled adapter do not crash the paid callback.
> 
> All SDK callbacks are forwarded to the app delegate after tracking (or
skipped when Purchases is not configured). README documents the expanded
event set, display triggers, and forwarding behavior.
> 
> Tests add precision mapping coverage, behavioral tests for
delegate/placement/response-info timing, and contract tests driven by
fixtures that require every SDK callback to be overridden, forwarded,
and to emit exactly the declared tracking event (or none).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
138ff80. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@RevenueCat-Danger-Bot

RevenueCat-Danger-Bot commented Aug 19, 2026

Copy link
Copy Markdown
2 Warnings
⚠️ Size check is being bypassed due to the presence of the label "danger-bypass-size-limit"
⚠️ Size increase: 500.86 KB
1 Message
📖 This PR changes 4617 lines of production Kotlin/Java (limit 300); skipped via skip-pr-lines-changed-check label.
Public API changes in feature:admob-next-gen (68 new declarations)
+ public final class AdTrackerAdMobExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackAppOpenAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.common.AdR…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackAppOpenAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.common.AdRequest adRe…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackAppOpenAdFromResponse(com.revenuecat.purchases.ads.events.AdTracker, String adResponse, String adUnitId, optional String? …
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackBannerAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.banner.AdView adView, …
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackBannerAdFromResponse(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackBannerAdFromResponse(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.banner.AdV…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackInterstitialAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.common.AdRequest…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackInterstitialAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.commo…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackInterstitialAdFromResponse(com.revenuecat.purchases.ads.events.AdTracker, String adResponse, String adUnitId, optional Str…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackNativeAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.nativead.Na…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackNativeAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdReque…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackNativeAdFromResponse(com.revenuecat.purchases.ads.events.AdTracker, String adResponse, String adUnitId, optional String? p…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackNativeAds(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.nativead.N…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackNativeAds(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdRequ…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackRewardedAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.common.AdRequest adR…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackRewardedAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.common.Ad…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackRewardedAdFromResponse(com.revenuecat.purchases.ads.events.AdTracker, String adResponse, String adUnitId, optional String?…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackRewardedInterstitialAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.sdk.common.A…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackRewardedInterstitialAd(com.revenuecat.purchases.ads.events.AdTracker, com.google.android.libraries.ads.mobile.s…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackRewardedInterstitialAdFromResponse(com.revenuecat.purchases.ads.events.AdTracker, String adResponse, String adUnitId, opti…
+ public final class AdViewExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackAd(com.google.android.libraries.ads.mobile.sdk.banner.AdView, com.google.android.libraries.ads.mobile.sdk.banner.BannerAdR…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static suspend Object? loadAndTrackAdFromResponse(com.google.android.libraries.ads.mobile.sdk.banner.AdView, String adResponse, String adUnitId, o…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void loadAndTrackAdFromResponse(com.google.android.libraries.ads.mobile.sdk.banner.AdView, String adResponse, String adUnitId, optional Str…
+ public final class AppOpenAdExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void setTrackingAdEventCallback(com.google.android.libraries.ads.mobile.sdk.appopen.AppOpenAd, com.google.android.libraries.ads.mobile.sdk.…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.appopen.AppOpenAd, android.app.Activity activity, String? placement);
+ public final class AppOpenAdPreloaderExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static com.google.android.libraries.ads.mobile.sdk.appopen.AppOpenAd? pollAndTrackAd(com.google.android.libraries.ads.mobile.sdk.appopen.AppOpenAd…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static boolean startAndTrack(com.google.android.libraries.ads.mobile.sdk.appopen.AppOpenAdPreloader.Companion, String preloadId, com.google.androi…
+ public final class BannerAdExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void setTrackingAdEventCallback(com.google.android.libraries.ads.mobile.sdk.banner.BannerAd, com.google.android.libraries.ads.mobile.sdk.ba…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void setTrackingBannerAdRefreshCallback(com.google.android.libraries.ads.mobile.sdk.banner.BannerAd, com.google.android.libraries.ads.mobil…
+ public final class BannerAdPreloaderExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static com.google.android.libraries.ads.mobile.sdk.banner.BannerAd? pollAndTrackAd(com.google.android.libraries.ads.mobile.sdk.banner.BannerAdPrel…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static boolean startAndTrack(com.google.android.libraries.ads.mobile.sdk.banner.BannerAdPreloader.Companion, String preloadId, com.google.android.…
+ public final class InterstitialAdExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void setTrackingAdEventCallback(com.google.android.libraries.ads.mobile.sdk.interstitial.InterstitialAd, com.google.android.libraries.ads.m…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.interstitial.InterstitialAd, android.app.Activity activity, String? placement);
+ public final class InterstitialAdPreloaderExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static com.google.android.libraries.ads.mobile.sdk.interstitial.InterstitialAd? pollAndTrackAd(com.google.android.libraries.ads.mobile.sdk.interst…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static boolean startAndTrack(com.google.android.libraries.ads.mobile.sdk.interstitial.InterstitialAdPreloader.Companion, String preloadId, com.goo…
+ public final class NativeAdExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void setTrackingAdEventCallback(com.google.android.libraries.ads.mobile.sdk.nativead.CustomNativeAd, com.google.android.libraries.ads.mobil…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void setTrackingAdEventCallback(com.google.android.libraries.ads.mobile.sdk.nativead.NativeAd, com.google.android.libraries.ads.mobile.sdk.…
+ public final class NativeAdPreloaderExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdLoadResult.NativeAdLoadSuccessResult? pollAndTrackAd(com.google.android.librar…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static boolean startAndTrack(com.google.android.libraries.ads.mobile.sdk.nativead.NativeAdPreloader.Companion, String preloadId, com.google.androi…
+ public final class RewardVerificationExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void enableRewardVerification(com.google.android.libraries.ads.mobile.sdk.rewarded.RewardedAd);
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void enableRewardVerification(com.google.android.libraries.ads.mobile.sdk.rewardedinterstitial.RewardedInterstitialAd);
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.rewarded.RewardedAd, android.app.Activity activity, String? placement, optional kotli…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.rewarded.RewardedAd, android.app.Activity activity, optional kotlin.jvm.functions.Fun…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.rewardedinterstitial.RewardedInterstitialAd, android.app.Activity activity, String? p…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.rewardedinterstitial.RewardedInterstitialAd, android.app.Activity activity, optional …
+ public final class RewardedAdExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void setTrackingAdEventCallback(com.google.android.libraries.ads.mobile.sdk.rewarded.RewardedAd, com.google.android.libraries.ads.mobile.sd…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.rewarded.RewardedAd, android.app.Activity activity, String? placement, com.google.and…
+ public final class RewardedAdPreloaderExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static com.google.android.libraries.ads.mobile.sdk.rewarded.RewardedAd? pollAndTrackAd(com.google.android.libraries.ads.mobile.sdk.rewarded.Reward…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static boolean startAndTrack(com.google.android.libraries.ads.mobile.sdk.rewarded.RewardedAdPreloader.Companion, String preloadId, com.google.andr…
+ public final class RewardedInterstitialAdExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void setTrackingAdEventCallback(com.google.android.libraries.ads.mobile.sdk.rewardedinterstitial.RewardedInterstitialAd, com.google.android…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static void show(com.google.android.libraries.ads.mobile.sdk.rewardedinterstitial.RewardedInterstitialAd, android.app.Activity activity, String? p…
+ public final class RewardedInterstitialAdPreloaderExtensionsKt
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static com.google.android.libraries.ads.mobile.sdk.rewardedinterstitial.RewardedInterstitialAd? pollAndTrackAd(com.google.android.libraries.ads.mo…
+ method @KotlinOnly @kotlin.jvm.JvmSynthetic public static boolean startAndTrack(com.google.android.libraries.ads.mobile.sdk.rewardedinterstitial.RewardedInterstitialAdPreloader.Companion, String pr…

Generated by 🚫 Danger

### Motivation

Google reports preload completion while filling a buffer, before the app
adopts an ad from that buffer. The format-specific preloader extensions
need one shared callback that preserves this boundary consistently.

This is the foundation of the Next-Gen AdMob preloading stack and is
stacked on #3957.

### Description

- Add an internal preload callback that records every preload success or
failure before forwarding it to the app.
- Forward `onAdsExhausted` without recording a RevenueCat ad event.
- Support a missing app delegate while always giving Google the internal
callback.
- Add shared tests and fixtures for callback forwarding and preload
outcome tracking.

This PR adds no public API and no polling abstraction. Format-specific
PRs install lifecycle tracking when an ad is polled, without
synthesizing another loaded event.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- Full-stack detekt and API validation

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Internal callback wrapper and unit tests only; analytics boundary
matches existing load tracking with no new public surface.
> 
> **Overview**
> Adds the shared **preload** tracking layer for Next-Gen AdMob so
buffer fills are recorded before any app callback runs.
> 
> **`TrackingPreloadCallback`** wraps Google’s `PreloadCallback`: it
emits RevenueCat **loaded** / **failed-to-load** events (via the same
helpers as direct loads) on `onAdPreloaded` and `onAdFailedToPreload`,
then forwards to an optional delegate. **`onAdsExhausted`** is forwarded
only—no ad event.
> 
> Introduces **`PreloaderTest`** with `Purchases`/`AdTracker` setup,
preload assertions, and a recording delegate for follow-on
format-specific preloader tests. **`TrackingPreloadCallbackTest`**
covers ordering (track before delegate), exhaustion passthrough, and
null delegates.
> 
> No public API; polling/lifecycle tracking for adopted ads is left to
later PRs.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
7a71e67. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Motivation

Banner preload completion and banner adoption happen at different times.
Tracking should record the former once while still installing lifecycle
tracking when the app polls the buffered banner.

This PR is stacked on #3995.

### Description

- Add `BannerAdPreloader.startAndTrack` to record every preload success
or failure and forward all Google callbacks.
- Add `pollAndTrackAd` to install banner event and refresh callbacks
without recording another loaded event.
- Keep preload-time and poll-time placements independent.
- Continue using Google's normal `AdView.registerBannerAd` after
polling.
- Document both tracked and untracked banner preloading flows.

If preloading starts through Google's plain API, polling can track later
lifecycle events but cannot retroactively report the original load.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew detektAll`
- `./scripts/api-check.sh`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Adds optional tracking wrappers around Google's preloader APIs without
changing core Purchases or auth; misconfiguration mainly affects ad
analytics completeness, not app security or purchases.
> 
> **Overview**
> Adds RevenueCat ad-event tracking for Google Mobile Ads Next-Gen
**banner preloading**, split across preload start and later poll/adopt.
> 
> **`BannerAdPreloader.startAndTrack`** wraps Google's `start` with
`TrackingPreloadCallback` so preload success and failure are recorded
once (with an optional **start-time** placement), while still forwarding
all Google preload callbacks.
> 
> **`pollAndTrackAd`** polls the buffer and installs
`TrackingBannerAdEventCallback` / `TrackingBannerAdRefreshCallback` for
display, click, revenue, and refresh—using a separate optional
**poll-time** placement—**without** emitting another Ad Loaded event.
Apps still register the banner via Google's `AdView.registerBannerAd`.
> 
> Public API is exposed under `RCAdMobNextGenPreloaders` (experimental,
Kotlin-only). README documents the two-stage placement model and the
case where preloading used Google's plain `start` (lifecycle tracking
only, no retroactive load).
> 
> Unit tests cover preload tracking, null poll, and poll-time
lifecycle/refresh with independent placement.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
c7e45c4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@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

…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 -->
### Motivation

Interstitial preload completion should be tracked when Google fills the
buffer, while polling should only adopt the ad and prepare its later
lifecycle tracking.

### Description

- Add `InterstitialAdPreloader.startAndTrack` to record every preload
success or failure and forward all Google callbacks.
- Add `pollAndTrackAd` to install the interstitial event callback
without recording another loaded event.
- Keep preload-time and poll-time placements independent.
- Preserve the existing show-time placement override.
- Document both tracked and untracked interstitial preloading flows.

If preloading starts through Google's plain API, polling can track later
lifecycle events but cannot retroactively report the original load.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew detektAll`
- `./scripts/api-check.sh`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Additive adapter extensions and tests only; no changes to core
purchases, auth, or payment logic.
> 
> **Overview**
> Adds **interstitial preloading** to the AdMob Next-Gen adapter,
matching the existing banner preload flow: preload success/failure is
tracked at start, and polling only wires lifecycle tracking without a
second loaded event.
> 
> **`InterstitialAdPreloader.startAndTrack`** wraps Google's `start`
with `TrackingPreloadCallback` for `AdFormat.INTERSTITIAL`, forwarding
all preload callbacks. **`pollAndTrackAd`** polls the buffer and sets
`TrackingInterstitialAdEventCallback` on the ad; preload-time and
poll-time placements stay independent, and show-time placement override
behavior is unchanged.
> 
> Public API surface is updated in `api.txt`. README documents the
interstitial preload + poll + show flow. Unit tests cover start
tracking, null poll, and poll lifecycle wiring (including no duplicate
`trackAdLoaded` on poll).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
fbba7ca. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Motivation

App-open preload completion should be recorded while Google fills the
buffer, independently from adopting and later showing the buffered ad.

### Description

- Add `AppOpenAdPreloader.startAndTrack` to record every preload success
or failure and forward all Google callbacks.
- Add `pollAndTrackAd` to install app-open lifecycle tracking without
recording another loaded event.
- Keep preload-time and poll-time placements independent while retaining
the show-time override.
- Document both tracked and untracked app-open preloading flows.

If preloading starts through Google's plain API, polling can track later
lifecycle events but cannot retroactively report the original load.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew detektAll`
- `./scripts/api-check.sh`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Additive adapter extensions and tests; no changes to core purchases,
auth, or payment flows.
> 
> **Overview**
> Adds **app-open preloading** to the AdMob Next-Gen adapter, matching
the existing banner and interstitial preload APIs.
> 
> `AppOpenAdPreloader.startAndTrack` wraps Google's `start` with
`TrackingPreloadCallback` so preload success and failure are recorded
(with an optional start-time `placement`) while still forwarding
Google's preload callbacks. `pollAndTrackAd` polls the buffer, installs
`TrackingAppOpenAdEventCallback` for show/click/revenue lifecycle, and
**does not** emit a second loaded event—that load is attributed at
preload time. Poll-time `placement` stays independent from start-time
placement.
> 
> Public API surface is updated in `api.txt`, README documents the
app-open preload flow, and unit tests cover preload tracking, null poll,
and lifecycle wiring without duplicate load tracking.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5d83402. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Motivation

Rewarded preload completion should be recorded while Google fills the
buffer, independently from adopting and later showing the buffered ad.

### Description

- Add `RewardedAdPreloader.startAndTrack` to record every preload
success or failure and forward all Google callbacks.
- Add `pollAndTrackAd` to install rewarded lifecycle tracking without
recording another loaded event.
- Keep preload-time and poll-time placements independent while retaining
the show-time override.
- Document both tracked and untracked rewarded preloading flows.

If preloading starts through Google's plain API, polling can track later
lifecycle events but cannot retroactively report the original load.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew detektAll`
- `./scripts/api-check.sh`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Follows the existing interstitial/banner preloader extension pattern
with no new security or data paths beyond ad event tracking.
> 
> **Overview**
> Extends the AdMob Next-Gen preloader tracking pattern to **rewarded**
ads: `RewardedAdPreloader.startAndTrack` records preload success/failure
via `TrackingPreloadCallback` (`AdFormat.REWARDED`), and
`pollAndTrackAd` attaches `TrackingRewardedAdEventCallback` for
show/click/revenue without emitting a second loaded event.
> 
> Public API surface in `RCAdMobNextGenPreloaders` and the README now
document the rewarded preload → poll → show flow (including independent
start vs poll placements). Unit tests cover preload wiring, null poll,
and lifecycle callback installation.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f8ec3e4. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Motivation

Rewarded-interstitial preload completion should be recorded while Google
fills the buffer, independently from adopting and later showing the
buffered ad.

### Description

- Add `RewardedInterstitialAdPreloader.startAndTrack` to record every
preload success or failure and forward all Google callbacks.
- Add `pollAndTrackAd` to install rewarded-interstitial lifecycle
tracking without recording another loaded event.
- Keep preload-time and poll-time placements independent while retaining
the show-time override.
- Document both tracked and untracked rewarded-interstitial preloading
flows.

If preloading starts through Google's plain API, polling can track later
lifecycle events but cannot retroactively report the original load.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew detektAll`
- `./scripts/api-check.sh`


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Additive adapter extensions parallel to existing rewarded/interstitial
preloaders; no changes to core purchase or auth logic.
> 
> **Overview**
> Extends the AdMob Next-Gen preloader API so **rewarded-interstitial**
ads follow the same two-stage RevenueCat tracking as other full-screen
formats.
> 
> **`RewardedInterstitialAdPreloader.startAndTrack`** wraps Google’s
preload `start` with `TrackingPreloadCallback`
(`AdFormat.REWARDED_INTERSTITIAL`), recording preload success/failure
and forwarding Google callbacks. **`pollAndTrackAd`** polls the buffer,
installs `TrackingRewardedInterstitialAdEventCallback` for
show/click/revenue lifecycle, and deliberately does **not** emit a
second loaded event when the ad is adopted.
> 
> Preload-time and poll-time **placements** stay independent. Public API
surface is updated in `api.txt`; README documents the
rewarded-interstitial preload flow. Unit tests cover preload tracking,
null poll, and lifecycle callback wiring without duplicate load
tracking.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
72a1c75. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Motivation

Native preloading can return native, custom-native, or banner fallback
results. Preload completion should be recorded once, while polling
should install the lifecycle callbacks appropriate for the returned
result.

### Description

- Add `NativeAdPreloader.startAndTrack` to record every preload success
or failure and forward all Google callbacks.
- Add `pollAndTrackAd` without recording another loaded event.
- Install native loader and event callbacks on native and custom-native
results.
- Install banner event and refresh callbacks on banner fallback results.
- Keep preload-time and poll-time placements independent.
- Document both tracked and untracked native preloading flows.

If preloading starts through Google's plain API, polling can track later
lifecycle events but cannot retroactively report the original load.

### Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest`
- `./gradlew detektAll`
- `./scripts/api-check.sh`


<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> New adapter extension following established preloader tracking
patterns; no auth or payment paths touched.
> 
> **Overview**
> Adds RevenueCat tracking for **Google Mobile Ads Next-Gen native ad
preloading**, matching the other preload integrations.
> 
> `NativeAdPreloader.startAndTrack` records preload success and failure
via `TrackingPreloadCallback` (`AdFormat.NATIVE`) and forwards Google
preload callbacks. `pollAndTrackAd` adopts a buffered result and wires
lifecycle tracking **without** emitting a second loaded event (load is
attributed at start).
> 
> Unlike single-ad-type preloaders, Google's native buffer can return
**native**, **custom-native**, or **banner** success variants.
`pollAndTrackAd` branches on `NativeAdLoadResult` and installs
`TrackingNativeAdEventCallback` on native/custom-native ads, or banner
event plus refresh callbacks when the fallback is a banner.
> 
> Preload-time and poll-time **placements stay independent**, consistent
with the rest of the module. Public API surface and README native
preloading examples are updated; unit tests cover start tracking, null
poll, and all three poll result types.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
b29ee9a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

- add an internal AdLoadResult helper that shares loaded/failed tracking
for suspending Google Mobile Ads Next-Gen APIs
- configure successful ads after the tracking attempt while returning
the exact original result instance
- preserve lazy response-info lookup and tracking-failure resilience,
with focused success/failure contract tests

## Context

This is a foundation-only change for later adoption by the
format-specific suspending loaders.

## Test plan

- ./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest
- ./gradlew detektAll
- ./scripts/api-check.sh

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Internal tracking helper plus unit tests; no public API or production
call-site changes yet.
> 
> **Overview**
> Adds `AdLoadResult.trackAndConfigureAdLoadResult` so suspending
Next-Gen loaders can reuse the same loaded/failed tracking as
`TrackingAdLoadCallback`.
> 
> On success it tracks then runs `configureAd`; on failure it only
tracks. It always returns the original result instance, and tracking
still goes through `trackIfConfigured` so missing SDK setup or tracker
errors do not skip ad configuration. Unit tests cover success, failure,
and those resilience cases.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1690bc2. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- Thank you for contributing to Purchases! Before pressing the
"Create Pull Request" button, please provide the following: -->

### Checklist
- [x] If applicable, unit tests
- [ ] If applicable, create follow-up issues for `purchases-ios` and
hybrids

### Motivation
Core emits reward-verification events only when an adapter supplies
tracking metadata, while the next-generation AdMob adapter currently
polls without it.

### Description
Passes ad metadata from RevenueCat tracking callbacks into
reward-verification polling with `AdCaptureMethod.ADAPTER`, preserving
the latest placement while leaving untracked ads unchanged.
Tests cover tracked and untracked metadata handoff, adapter capture
attribution, and runtime forwarding; validated with the full next-gen
unit-test suite, `detektAll`, and `scripts/api-check.sh`.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> Changes the reward-verification poll path and when analytics events
fire, but verification outcomes stay the same and untracked ads are
unchanged.
> 
> **Overview**
> Next-gen AdMob **reward verification polling** now supplies
**`RewardedAdTrackingMetadata`** when the ad was loaded through
RevenueCat’s tracking APIs, so core can emit reward-verification
analytics the same way it does when adapters provide metadata.
> 
> On reward earned, **`RewardVerificationManager`** reads metadata from
the installed **`TrackingRewardedAdEventCallback`** /
**`TrackingRewardedInterstitialAdEventCallback`** (null for untracked
ads). **`RewardVerificationRuntime`** forwards that metadata into
**`awaitPollRewardVerification`** with **`AdCaptureMethod.ADAPTER`**
instead of polling without metadata / manual capture.
**`TrackingAdEventCallback.rewardTrackingMetadata()`** builds the
payload at reward time (current placement and response info).
> 
> Tests assert untracked ads still pass null metadata, adapter capture
method, and show-time placement for rewarded and rewarded-interstitial
formats.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
de00254. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Motivation

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

## Description

- Add `AdView.loadAndTrackAd` and `AdTracker.loadAndTrackBannerAd` for
request-based loading through the recommended Next-Gen `AdView` API.
- Track successful and failed loads while preserving Google callbacks.
- Install lifecycle and refresh tracking before a loaded banner reaches
application code.
- Track automatic refresh successes and failures against the current
creative and original load placement.
- Add tracking-safe setters for banner event and refresh callbacks.
- Publish and document the experimental Kotlin API.

## Tracking behavior

The load-time placement is used throughout the banner lifecycle,
including automatic refreshes. Banner display is tracked from
`onAdImpression`; full-screen overlay callbacks are forwarded without
also counting a display. RevenueCat tracking runs before application
callbacks.

Assigning `adEventCallback` or `bannerAdRefreshCallback` directly after
a tracked load replaces the RevenueCat wrapper. Applications should
provide callbacks while loading or use the tracking-safe setters.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Additive experimental API on the AdMob Next-Gen adapter with tests;
main integration risk is apps bypassing tracking-safe setters and
dropping analytics.
> 
> **Overview**
> Adds **RevenueCat ad tracking for Google Mobile Ads Next-Gen banners**
loaded through `AdView`, complementing the existing banner preloader
helpers.
> 
> New entry points are **`AdView.loadAndTrackAd`** and
**`AdTracker.loadAndTrackBannerAd`**. They wrap `AdView.loadAd` with
`TrackingAdLoadCallback`, install lifecycle and refresh wrappers on the
loaded `BannerAd` before the app’s load callback runs, and emit load
success/failure (including placement and ad unit) while still forwarding
Google callbacks.
> 
> **`BannerAd.setTrackingAdEventCallback`** and
**`setTrackingBannerAdRefreshCallback`** let apps swap delegates without
replacing RevenueCat’s wrappers. Shared **`installTrackingCallbacks`**
is extracted so **`BannerAdPreloader.pollAndTrackAd`** uses the same
path as direct loads.
> 
> Public **`api.txt`** and README cover banner placement (sticky for
refreshes), usage examples, and the warning not to assign callbacks
directly. **`BannerAdFlowTest`** covers load tracking order, safe
callback replacement, and failed-load attribution.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
2fb60b8. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Motivation

Expose server-to-server Google Mobile Ads Next-Gen banner response
loading through the RevenueCat tracking API.

This PR is stacked on #4014, which provides the core banner tracking
integration and callback installation.

## Description

- Add callback-based `AdView.loadAndTrackAdFromResponse` and
`AdTracker.loadAndTrackBannerAdFromResponse` APIs.
- Add suspending overloads that return the original Google
`AdLoadResult` unchanged.
- Require an explicit ad unit ID so successful and failed response loads
retain attribution.
- Install lifecycle and refresh tracking before loaded banners reach
application code.
- Preserve application callbacks while tracking load success and
failure.
- Publish and document the experimental response-loading API.

## Tracking behavior

The supplied placement and ad unit ID are used for response load events
and subsequent banner lifecycle events. RevenueCat tracking and callback
installation happen before application callbacks or suspending results
are returned.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Additive experimental API on top of existing banner tracking patterns,
with focused unit tests and no changes to core purchase or auth flows.
> 
> **Overview**
> Adds **server-to-server banner loading** with RevenueCat tracking for
Google Mobile Ads Next-Gen, matching the existing `FromResponse` APIs on
other formats.
> 
> New public entry points are `AdView.loadAndTrackAdFromResponse` and
`AdTracker.loadAndTrackBannerAdFromResponse`, each with callback and
suspending overloads. Callers must pass an explicit **`adUnitId`** (and
optional placement) so load success and failure events keep correct
attribution when the opaque response does not expose it.
> 
> Implementation wires `AdView.loadFromAdResponse` through the same
tracking path as request-based banner loads: `TrackingAdLoadCallback`
for callbacks, `trackAndConfigureAdLoadResult` for suspend, with
lifecycle and refresh wrappers installed before app code sees the loaded
`BannerAd`. Suspending APIs return Google’s `AdLoadResult` unchanged.
> 
> README and `api.txt` document the new surface;
`BannerAdResponseFlowTest` covers callback ordering, failure
attribution, and suspending success/failure behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
9a172fc. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Motivation

Expose Google Mobile Ads Next-Gen single native-ad loading through a
public RevenueCat tracking API.

## Description

- Add callback-based and suspending `AdTracker.loadAndTrackNativeAd`
APIs for request-based loading through Google's Next-Gen
`NativeAdRequest` API.
- Add callback-based `loadAndTrackNativeAdFromResponse` for
server-to-server ad responses, with an explicit ad unit ID for
attribution.
- Track and configure every result a native request can produce:
standard native, custom-native, banner, and failure results.
- Install lifecycle and paid-event tracking before a loaded ad reaches
application code or is emitted downstream.
- Add tracking-safe callback setters for standard native, custom-native,
and banner results.
- Publish and document the experimental Kotlin API.

## Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest --tests
'com.revenuecat.purchases.admob.nextgen.NativeAdFlowTest' --tests
'com.revenuecat.purchases.admob.nextgen.NativeAdResponseFlowTest'`
- `./gradlew :feature:admob-next-gen:lintDefaultsDebug`
- `./gradlew detektAll`
- `./scripts/api-check.sh`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Medium Risk**
> New experimental public APIs affect ad revenue attribution and event
ordering; incorrect callback assignment could drop tracking, but
patterns mirror existing formats and are covered by tests.
> 
> **Overview**
> Adds **RevenueCat ad tracking** for Google Mobile Ads Next-Gen
**native loads**, covering direct `NativeAdRequest` loads, suspending
loads, and **server-to-server** responses via new
`AdTracker.loadAndTrackNativeAd` / `loadAndTrackNativeAdFromResponse`
APIs (published in `api.txt`).
> 
> Each `NativeAdLoadResult` path is handled explicitly: standard and
custom-native ads get native-format load events and
`TrackingNativeAdEventCallback`; banner results from a native request
are tracked as **banner** with the usual banner wrappers. Tracking is
installed **before** the app’s load callback runs;
`TrackingNativeAdLoaderCallback` now invokes configure hooks for all
three success types.
> 
> Introduces **`NativeAdExtensions`** with `setTrackingAdEventCallback`
on `NativeAd` and `CustomNativeAd` so apps can swap delegates without
stripping RC wrappers. **Native preloader** `pollAndTrackAd` is
refactored to reuse the same `installTrackingEventCallback` /
`installTrackingCallbacks` helpers instead of duplicated private setup.
> 
> README gains native placement guidance, usage examples, and
supported-format table entries; **unit tests** cover
callback/suspend/response flows and tracking-safe setters.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
f3f4046. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Motivation

Complete the Google Mobile Ads Next-Gen native-ad integration by
supporting Google's multiple-ad loading APIs.

## Description

- Add callback-based `AdTracker.loadAndTrackNativeAds` for batches of up
to five ads.
- Add a suspending overload that returns Google's original
`Flow<NativeAdLoadResult>`.
- Track and configure every standard native, custom-native, banner, or
failure result before forwarding it.
- Forward `NativeAdLoaderCallback.onAdLoadingCompleted` after the batch
finishes.
- Publish and document the experimental batch-loading APIs.

## Validation

- `./gradlew :feature:admob-next-gen:testDefaultsDebugUnitTest --tests
'com.revenuecat.purchases.admob.nextgen.NativeAdBatchFlowTest'`
- `./scripts/api-check.sh`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Experimental admob-next-gen extension APIs that reuse existing native
tracking helpers; no auth or core purchase-path changes.
> 
> **Overview**
> Adds **batch native ad loading** on `AdTracker` for Google Mobile Ads
Next-Gen, mirroring the existing single-ad `loadAndTrackNativeAd`
behavior across multiple results in one request.
> 
> New **`loadAndTrackNativeAds`** overloads accept `maxNumberOfAds` and
either a **`NativeAdLoaderCallback`** or a suspending
**`Flow<NativeAdLoadResult>`**. Each standard native, custom-native,
banner success, or failure is **tracked and configured** (RevenueCat
load/fail events plus tracking event callbacks on ads) before it reaches
the app callback or flow collector; the callback path also forwards
**`onAdLoadingCompleted`** when the batch finishes.
> 
> The experimental public API is reflected in **`api.txt`**, with
**`NativeAdBatchFlowTest`** covering callback forwarding and flow
collection.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
3cc9849. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Scaffolds a focused example app for RevenueCat tracking with Google
Mobile Ads Next-Gen:

- add the next-generation AdMob sample module and project wiring
- initialize RevenueCat and Google Mobile Ads for the sample
- add navigation for the format-focused examples

## Screenshots

![AdMob Next-Gen sample home
screen](https://github.com/RevenueCat/purchases-android/blob/f8ef2584ca2a5a36c4e5831be6e72245ac87f448/gh-pr-images/pr-4093/1787759286-26075-1-01-home.png?raw=true)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> New isolated example module with no changes to core SDK behavior;
format demos are stubs only.
> 
> **Overview**
> Adds a new **`examples/admob-next-gen-sample`** Compose app and
registers it in **`settings.gradle.kts`**, aimed at demonstrating
RevenueCat ad-event tracking with the **Google Mobile Ads Next-Gen SDK**
via `:purchases` and `:feature:admob-next-gen`.
> 
> The scaffold wires **RevenueCat** and **Mobile Ads** initialization in
`MainApplication` (with home-screen readiness state), reads
**`REVENUECAT_API_KEY`** and optional AdMob IDs from root
**`local.properties`** into **`BuildConfig`**, and provides a home
screen that navigates to per-format routes (banner, interstitial, app
open, rewarded, native, diagnostics). **Format screens are
placeholders** for follow-up work; shared UI (`AdScreen`
Direct/Preloaded chips, `StatusCard`, `ActionRow`) and a **native ad
layout** are included for later layers. The README documents local run
steps and the intended tracking coverage.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
0e2d679. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Expands the Google Mobile Ads Next-Gen example app with reusable
preloader controls:

- add reusable controls for starting and stopping format preloaders
- expose buffer capacity and available-ad state
- keep buffer configuration editable only before a preloader starts

## Screenshots

![Running preloader with configurable capacity and two ads
ready](https://github.com/RevenueCat/purchases-android/blob/8ea99f14bda69624087a64460ff16065f70c1432/gh-pr-images/pr-4094/1787759309-26910-1-04-preloader-started.png?raw=true)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Sample-app-only UI helpers with no changes to SDK, billing, or
production ad integration paths.
> 
> **Overview**
> Adds **reusable Compose UI** for the AdMob Next-Gen sample so format
screens can demo ad preloading without duplicating layout code.
> 
> **`PreloaderUi.kt`** introduces `PreloaderUiState` (backed by
`PreloadConfiguration` and an ads-available provider),
`rememberPreloaderUiState`, and `PreloaderPanel`: start/stop toggle,
running/stopped badge, buffer capacity steppers (1–5, locked while
running), live “ads ready” count with ~500ms polling, status messages
after start/stop/preload events, plus optional extra metrics and action
buttons.
> 
> **`AdSupport.kt`** adds `preloadStatusCallback`, a small
`PreloadCallback` that hops preload success, failure, and
buffer-exhausted events onto a `CoroutineScope` so the panel can update
safely from SDK callbacks.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
5868b70. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Expands the Google Mobile Ads Next-Gen example app with banner
workflows:

- add direct and preloaded banner workflows
- reset rendered banner state when switching load modes
- demonstrate registration and refresh callbacks

## Screenshots

![Banner direct-loading
screen](https://github.com/RevenueCat/purchases-android/blob/ad50aeae8f85add900cde71a826177c8bc42424e/gh-pr-images/pr-4095/1787759316-27587-1-02-banner-direct.png?raw=true)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Sample-app and documentation changes only; no production SDK or
auth/data-path changes.
> 
> **Overview**
> Replaces the **Banner** placeholder in the AdMob Next-Gen sample with
a working **Banner** screen that shows RevenueCat-tracked banner flows
end to end.
> 
> **Direct** mode loads into an `AdView` via `loadAndTrackAd`, with
status for load success/failure plus **impression**, **click**, and
**refresh** callbacks. **Preloaded** mode uses the shared preloader UI
to **start/stop** buffering, then **poll + register** a buffered ad on
the same view with distinct placements (`banner_direct`,
`banner_preload`, `banner_poll`). Switching load modes recreates the
`AdView` (`key(mode)`) and resets status so banner state does not leak
across modes.
> 
> The sample README **Coverage** line for banners is updated to describe
lifecycle/refresh callbacks instead of callback-replacement wording.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1035d0b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Expands the Google Mobile Ads Next-Gen example app with full-screen ad
workflows:

- add direct and preloaded interstitial examples
- add direct and preloaded app-open examples
- demonstrate show-time placement overrides

## Screenshots

![Interstitial direct-loading screen with show-time placement
guidance](https://github.com/RevenueCat/purchases-android/blob/c8b88fe2a12c614d612e9f45892b2484aaae5743/gh-pr-images/pr-4096/1787759324-27985-1-05-fullscreen-interstitial.png?raw=true)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are confined to the example app’s UI and documentation-style
ad integration; no SDK or production code paths are modified.
> 
> **Overview**
> Replaces **interstitial** and **app open** placeholders in the AdMob
Next-Gen sample with real Compose flows wired through RevenueCat’s
`adTracker` and Next-Gen preload helpers.
> 
> Adds a shared **`FullScreenAdScreen`** that mirrors the banner
sample’s **direct load** vs **preload** modes: tracked load
(`loadAndTrackInterstitialAd` / `loadAndTrackAppOpenAd`),
`startAndTrack` / `pollAndTrackAd`, show/dismiss handling, and status
UI. Each format uses **different placement strings** at load, poll,
preload, and show so reviewers can see **show-time placement overrides**
(called out explicitly on the interstitial screen). App-open loading is
**manual** in the sample rather than tied to process lifecycle.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
e773e0c. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Expands the Google Mobile Ads Next-Gen example app with native ad
workflows:

- add direct native loading for single and batched ads
- add native preloading with configurable poll-and-render counts
- replace previously rendered native ads when loading again

## Screenshots

![Native preloader with automatic multi-ad
selection](https://github.com/RevenueCat/purchases-android/blob/c53f26b9837e0724805824f9568040e12b8d0462/gh-pr-images/pr-4097/1787759515-36192-1-06-native-preloaded.png?raw=true)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are confined to the example app and documentation, with no
production SDK or billing/auth impact.
> 
> **Overview**
> Replaces the **Native** placeholder in the AdMob Next-Gen sample with
a full **`NativeScreen`** that exercises RevenueCat’s native tracking
APIs end to end.
> 
> Users can switch between **standard and native-video test ad units**
(new `ADMOB_NATIVE_VIDEO_AD_UNIT_ID` in `local.properties` /
`BuildConfig`), then try **direct** loads via `loadAndTrackNativeAd` or
a **batch `Flow`** via `loadAndTrackNativeAds`, or **preloaded**
inventory with start/stop, buffer sizing, and **Poll + Show** using
`startAndTrack` / `pollAndTrackAd`. Loaded ads render in `NativeAdView`
cards; non-native result types are tracked and destroyed with explicit
status messaging, and in-flight loads clean up SDK-owned ads on cancel
or mode changes.
> 
> **`AdScreen`** gains an optional **`onModeChange`** hook so switching
Direct/Preloaded clears native UI state; README coverage notes mention
standard/video selection.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
80f65d6. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Expands the Google Mobile Ads Next-Gen example app with rewarded-ad and
diagnostics workflows:

- add direct and preloaded rewarded-ad workflows
- demonstrate optional RevenueCat reward verification
- add intentional direct and preload failures for diagnostics

## Screenshots

![Rewarded ad screen with RevenueCat reward verification
enabled](https://github.com/RevenueCat/purchases-android/blob/f03462f64f1d63d0f340bf6c8eb019daaaf8bb75/gh-pr-images/pr-4098/1787759523-36608-1-07-rewarded-verification.png?raw=true)

![Diagnostics screen for direct and preloaded
failures](https://github.com/RevenueCat/purchases-android/blob/8086b345654c5657ebac0caaf271841c00a678c5/gh-pr-images/pr-4098/1787759527-37108-1-08-diagnostics.png?raw=true)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Example-app UI only; no SDK or production code changes.
> 
> **Overview**
> Replaces placeholder routes in the AdMob Next-Gen sample with
**Rewarded** and **Diagnostics** screens wired through `SampleUi`.
> 
> **Rewarded** adds the same direct-vs-preloaded flow as other formats:
load/show via `loadAndTrackRewardedAd`, preloader `startAndTrack` /
`pollAndTrackAd`, and a toggle to enable RevenueCat reward verification
on load/poll (verification vs Google’s `OnUserEarnedRewardListener` at
show time).
> 
> **Diagnostics** deliberately uses an invalid interstitial ad unit to
trigger expected load failures and surface RevenueCat failed-to-load
tracking for both direct load and interstitial preloading.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
d3604f1. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Completes the Google Mobile Ads Next-Gen example app with
rewarded-interstitial support and regression coverage:

- add direct and preloaded rewarded-interstitial workflows
- demonstrate optional RevenueCat reward verification
- add focused regression coverage for placement override callback
handling

## Screenshots

![Rewarded-interstitial preloader with reward verification enabled and
two ads
ready](https://github.com/RevenueCat/purchases-android/blob/b8ff8292759648e051c8bb6cf76e35f1e764fb79/gh-pr-images/pr-4045/1787759536-37402-1-09-rewarded-interstitial.png?raw=true)

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are limited to the example app, documentation, and unit tests;
no production adapter behavior is modified in this diff.
> 
> **Overview**
> Finishes the AdMob Next-Gen **sample app** by replacing the
rewarded-interstitial placeholder with a real screen and documenting how
to run and validate every format.
> 
> **Rewarded UI** is refactored into a shared generic `RewardedAdScreen`
used by both **Rewarded** and **Rewarded interstitial**. Each wires
RevenueCat **direct load**, **preload/poll**, and **show** with
format-specific placements, plus an optional **RevenueCat reward
verification** toggle (applied at load/poll time).
> 
> **Docs** cross-link the sample and adapter READMEs and add guidance on
direct vs preloaded flows, dashboard event checks, native test inventory
caveats, and main-thread handling for SDK callbacks.
> 
> **Tests:** new `PlacementOverrideTest` covers `applyPlacementOverride`
on tracking callbacks (update, clear, ignore non-tracking wrappers).
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
44b412b. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
## Summary

Adds CI coverage for the Google Mobile Ads Next-Gen example app:

- add a Fastlane lane that assembles the Next-Gen AdMob sample
- add a dedicated CircleCI job for the sample build
- run the job in pull-request and main-branch snapshot workflows

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Changes are limited to Fastlane and CircleCI wiring; no SDK, auth, or
runtime behavior is modified.
> 
> **Overview**
> Adds **CI coverage** for the Google Mobile Ads Next-Gen example
(`examples:admob-next-gen-sample`), mirroring the existing AdMob
integration sample pipeline.
> 
> A new Fastlane lane **`build_admob_next_gen_integration_sample`**
assembles the debug APK via
`:examples:admob-next-gen-sample:assembleDebug`. CircleCI gets a
matching **`assemble-admob-next-gen-integration-sample-app`** job (same
setup as the current AdMob sample job), wired into
**`build-test-deploy`** after `prepare-tests` and into
**`snapshot-deploy-sample-app-tests`** on main alongside the legacy
AdMob sample build.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
274017e. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
### Description

Adds a RevenueCat-specific migration guide for moving from the legacy
`purchases-admob` adapter to `purchases-admob-next-gen`.

### Changes

- Documents the dependency and package/import replacements.
- Explains required Next-Gen initialization and mediation exclusions.
- Maps legacy tracking entry points and callback types to their Next-Gen
equivalents.
- Clarifies why banners use `BannerAdRequest` instead of a generic
`AdRequest`.
- Covers callback threading, tracking-safe callback replacement, reward
verification, and placement continuity.

### Testing

- `git diff --check`

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> Documentation-only README update with no runtime, API, or build
changes.
> 
> **Overview**
> Adds a **Migrate from the legacy adapter** section to
`feature/admob-next-gen/README.md`, positioned before the existing
initialization docs.
> 
> The new guide states that `purchases-admob-next-gen` is not a drop-in
replacement and walks integrators through swapping legacy
`purchases-admob` and `play-services-ads` for the Next-Gen adapter and
`ads-mobile-sdk`, with a pointer back to mediation exclusions. It
documents package/import moves (`com.revenuecat.purchases.admob` →
`nextgen`, GMS ads → `com.google.android.libraries.ads.mobile.sdk`) and
ties initialization to `InitializationConfig` and the existing
initialize section.
> 
> A format-by-format table maps legacy vs Next-Gen RevenueCat tracking
entry points (notably banners via `BannerAdRequest`, native via
`AdTracker.loadAndTrackNativeAd(s)` instead of
`AdLoader.Builder.forNativeAdWithTracking`). The text explains
request/callback type changes (`AdLoadCallback`, format-specific event
callbacks), keeping `placement` and reward-verification helpers,
background-thread callbacks with main-thread UI updates, and using
RevenueCat load helpers / `setTrackingAdEventCallback` so tracking
wrappers are not replaced accidentally.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit
1d30f8a. Bugbot is set up for automated
code reviews on this repo. Configure
[here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
@polmiro
polmiro requested a review from tonidero September 1, 2026 15:25
@polmiro
polmiro marked this pull request as ready for review September 1, 2026 15:26
@polmiro
polmiro requested a review from a team as a code owner September 1, 2026 15:26

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.

Now that it's all put together this README is way too long and hard to follow. It could do with a table of contents at least, but a bunch of the header nesting really doesn't make sense

In general the sensation is, like some of the code, that we're explaning the same thing 6 times with different ad types rather than one cohesive API (placements, callback warnings, etc are repeated all over the place)

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.

3 participants