feat(admob): add remaining next-gen ad formats - #3962
Closed
polmiro wants to merge 37 commits into
Closed
Conversation
2 tasks
Danger has errored[!] Invalid from /tmp/d20260817-119-fjy7/temporary_danger.rb:1-------------------------------------------> <html><body><h1>503 Service Unavailable</h1> No server is available to handle this request.-------------------------------------------Generated by 🚫 Danger |
polmiro
force-pushed
the
add-next-gen-admob-banner-interstitial
branch
2 times, most recently
from
August 13, 2026 11:56
9af814b to
4eef8bb
Compare
polmiro
force-pushed
the
add-next-gen-admob-banner-interstitial
branch
from
August 17, 2026 08:33
4eef8bb to
74ad4e5
Compare
polmiro
force-pushed
the
add-next-gen-admob-remaining-formats
branch
from
August 17, 2026 08:33
ceb4647 to
adf3473
Compare
📸 Snapshot Test621 unchanged
🛸 Powered by Emerge Tools |
Set unitTests.isReturnDefaultValues so android.util.Log calls do not throw in JVM unit tests, matching the feature/admob module. Without it, any test reaching the not-configured branch of trackIfConfigured fails with "Method w in android.util.Log not mocked". Add coverage for that branch: TrackIfConfiguredTest for the guard itself, plus a test per callback wrapper asserting the delegate and configureAd still run when Purchases has not been configured.
Replace PurchasesTestHelper, which reflected on the internal backingFieldSharedInstance, with mockkObject(Purchases) plus stubs for the public isConfigured and sharedInstance members. This is what the rest of the codebase does from outside the :purchases module (CheckpointWorkflowActivityTest, PaywallDialogTests) and inside it (ScreenOfferingExtensionsTest). Reflection on a string field name only fails at runtime if the field is renamed; stubbing public API is checked at compile time. The reflection and @file:Suppress("INVISIBLE_REFERENCE") variants in feature/admob remain the only occurrences in the repo. The unconfigured-path tests now stub isConfigured to false rather than clearing the singleton.
The file opted into ExperimentalPreviewRevenueCatPurchasesAPI but touches no experimental API: trackIfConfigured is plain internal and Purchases is stable.
…perty The constructor took a plain delegate param and re-declared a property that shadowed it. TrackingFullScreenContentCallback in feature/admob, the existing precedent for a mutable delegate, declares it inline instead.
Both wrappers were asserted in one test method, so a failure on the load callback hid the banner refresh callback's result. They are now independent test methods. The shared helper is private since it is only used here.
The failure path deliberately does not configure the ad, but nothing pinned it. Note this documents intent rather than guarding a reachable regression: configureAd takes an AdT, which onAdFailedToLoad has no access to, so it could only start firing if the signature changed.
AdapterTracking and TrackIfConfigured are ports of feature/admob files that carry this KDoc; it was dropped in the port. The two callback wrappers had none, while their legacy counterparts document which callback maps to which event. Also records two things only the tests encoded: configureAd runs after tracking and before the delegate and never for a failed load, and responseInfoProvider is read when the callback fires because the SDK does not pass the refreshed ResponseInfo to onAdRefreshed.
The block was separated from the first function by a blank line, making it an orphan comment attached to nothing. It now documents the trackFromAdapter overloads.
Add an Events tracked section covering the two events this change reports, Ad Loaded and Ad Failed to Load, along with their payload fields and the callback-forwarding guarantee. Display, open and revenue events are documented in the event-tracking change that builds on this one.
Add the one-line description of the module and the Kotlin-only note. The placement guide, per-format usage examples, supported-formats table and experimental-API note previously staged here document the public loading APIs, which land in the changes above this one, so they move there.
TrackingAdEventCallback captured ResponseInfo at construction, so an auto-refreshing banner reported the first-loaded creative's impressionId and networkName for every later displayed/opened/revenue event, while TrackingBannerAdRefreshCallback emitted ad_loaded with fresh response info. Take a () -> ResponseInfo provider instead, matching the refresh callback and the legacy admob wrappers.
The precision mapping covered every PrecisionType in the pinned Google Mobile Ads SDK, so Kotlin compiled the when with an implicit else that throws NoWhenBranchMatchedException. The module ships as a precompiled artifact and declares the SDK with implementation, so an app resolving a newer SDK could deliver a constant this binary never saw and crash inside the paid-event callback. Map unrecognized constants to UNKNOWN, matching the legacy admob module. The exhaustiveness assertion in AdMobPrecisionMappingTest keeps failing at build time when the SDK grows a constant, so the mapping cannot silently fall behind.
…back The six format callbacks differ only in the AdFormat and AdDisplayedTrigger they pass to the base class, but only banner and interstitial were asserted. A wrong format, or an IMPRESSION trigger on a full-screen format (which would double-count displays, since the SDK fires onAdImpression alongside onAdShowedFullScreenContent), would have shipped silently. Replace the two single-format tests with a table covering all six, asserting the tracked format and that the other trigger tracks nothing. A companion test pins the table against the shared callback map so a new format cannot be added without covering it. Verified by mutation: flipping app open to AdFormat.REWARDED and to AdDisplayedTrigger.IMPRESSION each fail the table test.
assertOverridesAllSdkCallbacks only compares method signatures, so an override with an empty body passed. That left onAdShowedFullScreenContent, onAdDismissedFullScreenContent, onAdFailedToShowFullScreenContent, InterstitialAdEventCallback.onAppEvent, the rewarded interstitial metadata callback and every TrackingAppOpenAdEventCallback method unverified. Drive each SDK callback reflectively against a JDK proxy delegate that records the invoked method, with Purchases configured so the real tracking path runs first. This also pins that a throw on the tracking path cannot swallow the delegate call. A second test repeats the sweep with a null delegate to pin the no-callback path. Verified by mutation: dropping the onAdDismissedFullScreenContent forward fails the new test and nothing else.
FormatTrackingAdEventCallbacks was the only production file in the package opting in at file level. Every sibling, including AdMobPrecisionMapping added in the same PR, annotates the declaration. Narrow the scope to match.
onAdClicked, onAdPaid and trackAdDisplayed each repeated the configured check, the event-time response info read and the adTracker hop. Pull that into a private track helper so the callbacks carry only their payload, and so the event-time read is stated once instead of three times.
…ructor The base class took delegate and placement as plain parameters and then re-declared each as a property initialized from the parameter it shadowed. Overrides resolve to the property, so behaviour is unchanged, but inside an init block or a property initializer the bare name would silently read the parameter instead. Declare both as constructor properties, removing the shadowing and matching the legacy admob TrackingFullScreenContentCallback.
delegate and placement are vars so show-time code can swap them after the ad loaded, but nothing pinned that. The forwarding contract test cannot: a delegate snapshotted at construction still equals the one passed in, so it passes either way. Assert that swapping the delegate redirects both an inherited override and a format-specific one, that clearing it stops forwarding while tracking continues, and that display and click read placement at event time the way the paid callback already does. Verified by mutation: forwarding to a delegate captured at construction fails the two delegate tests, and a placement captured at construction fails the third.
ADS-536. FormatTrackingAdEventCallbacksTest was the last legacy-shaped holdover: one hand-written test per format asserting the delegate was called. The reflective sweep already drives every SDK method on all six wrappers, so the rewarded and native tests were pure duplicates. The banner test was not, because the sweep only asserted method names. Assert forwarded arguments too, with a distinct value per position so a swapped or dropped argument fails, and keep one focused test for the null app-event payload that reflection cannot supply. Then drop the file. Net coverage is higher, not lower: argument fidelity now applies to every callback on every wrapper rather than to onAppEvent alone. Verified by mutation: swapping onAppEvent's arguments fails both the sweep and the null-payload test.
Extend the Events tracked section with the three events this change adds, the per-format display trigger, and the event callbacks that are forwarded but not tracked. Scoped to what this change implements: the load events are documented in the load-tracking change below it, and the loading APIs that expose these callbacks are documented in the changes above it.
The flow tests asserted callback wiring and delegation but never installed a Purchases singleton, so trackIfConfigured short-circuited on every path and the load-time tracking arguments went unchecked. The wrapper classes are covered in TrackingAdEventCallbackTest, but nothing pinned what the banner and interstitial entry points hand to TrackingAdLoadCallback: swapping AdFormat.BANNER for INTERSTITIAL, dropping the placement, or not reading adUnitId off the request all kept the suite green.
The generated AdViewExtensionsKt-style names were leaking into api.txt and Dokka. Follow feature/admob's @file:JvmName convention, keeping NextGen in each name so the facades stay unambiguous next to the legacy module's.
polmiro
force-pushed
the
add-next-gen-admob-banner-interstitial
branch
from
August 17, 2026 10:29
74ad4e5 to
b64c6d1
Compare
polmiro
force-pushed
the
add-next-gen-admob-remaining-formats
branch
from
August 17, 2026 13:47
adf3473 to
71ad36a
Compare
polmiro
force-pushed
the
add-next-gen-admob-remaining-formats
branch
from
August 17, 2026 15:59
71ad36a to
9cc85de
Compare
polmiro
changed the base branch from
add-next-gen-admob-banner-interstitial
to
add-next-gen-admob-suspend-loading
August 17, 2026 16:00
polmiro
force-pushed
the
add-next-gen-admob-suspend-loading
branch
2 times, most recently
from
August 19, 2026 07:47
7ba09ad to
837f78c
Compare
polmiro
changed the base branch from
add-next-gen-admob-suspend-loading
to
add-next-gen-admob-interstitial
August 19, 2026 09:44
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
Complete Google Mobile Ads Next-Gen tracking coverage for the formats that follow banner and interstitial support.
Description
Stack
Validation
./gradlew :feature:admob-next-gen:build./gradlew detektAll./scripts/api-check.sh