Skip to content

Fix native ad API not being generated for ads-mobile-sdk#1507

Open
marius-bughiu wants to merge 1 commit into
dotnet:mainfrom
marius-bughiu:fix/ads-mobile-sdk-native-ads
Open

Fix native ad API not being generated for ads-mobile-sdk#1507
marius-bughiu wants to merge 1 commit into
dotnet:mainfrom
marius-bughiu:fix/ads-mobile-sdk-native-ads

Conversation

@marius-bughiu

Copy link
Copy Markdown
Contributor

Problem

Xamarin.Google.Android.Libraries.Ads.Mobile.Sdk (Google Mobile Ads Next-Gen SDK) ships with no usable native ad API. Missing from the generated assembly entirely:

  • NativeAd (the loaded-ad interface, with Headline/Body/Icon/MediaContent/…)
  • CustomNativeAd
  • MediaContent
  • NativeAdLoaderCallback.onNativeAdLoaded and onCustomNativeAdLoaded
  • NativeAdView.registerNativeAd

Without onNativeAdLoaded there is no way to receive a loaded native ad, so native ads cannot be used at all from .NET. NativeAdLoaderCallback binds with only onBannerAdLoaded, onAdFailedToLoad and onAdLoadingCompleted.

Root cause

MediaContent declares an obfuscated accessor:

public abstract com.google.android.libraries.ads.mobile.sdk.internal.nativead.InternalNativeAd a();

The …sdk.internal package is stripped by the existing remove-node in Metadata.xml, so this abstract interface method has an unresolvable return type and the whole MediaContent interface fails to generate.

That cascades. NativeAd.getMediaContent() and CustomNativeAd.getMediaContent() then reference an unbound type, so NativeAd and CustomNativeAd are dropped too — which silently drops onNativeAdLoaded / onCustomNativeAdLoaded (their parameter types no longer exist) and NativeAdView.registerNativeAd.

Fix

Remove the internal accessor, exactly like the existing BannerAd.getView() / BannerAd.a(AdSize) removals directly above it in the same file.

Surfacing those types then revealed a second, pre-existing generics-erasure conflict: NativeRequest declares getCustomFormatIds() / getNativeAdTypes() with a raw java.util.List return, while its implementors (NativeAdRequest, NativeSignalRequest) return the reified List<String> / List<NativeAd.NativeAdType>. In C# that is IList vs IList<T>, which cannot satisfy the interface member (CS0738):

error CS0738: 'NativeAdRequest' does not implement interface member 'INativeRequest.CustomFormatIds'.
'NativeAdRequest.CustomFormatIds' cannot implement 'INativeRequest.CustomFormatIds' because it does not
have the matching return type of 'IList'.

So the raw interface members are removed as well — the same fix already applied to BannerRequest.getAdSizes().

Verification

Built the binding and ran native ads end-to-end in a .NET MAUI app on an Android emulator (API 36):

  • NativeAdLoaderCallback.onNativeAdLoaded now fires with a bound NativeAd
  • NativeAdView.registerNativeAd(nativeAd, mediaView) renders the ad, including its MediaView
  • MediaContent.VideoController play/pause/mute and the video lifecycle callbacks work

Also verified banner, interstitial, rewarded, rewarded-interstitial and app-open still build and serve.

Notes

  • nugetVersion bumped 1.2.1.11.2.1.2.
  • Regenerating PublicAPI.Unshipped.txt also picked up API that already shipped in 1.2.1.1 but was not recorded in the file: AgeRestrictedTreatment, the IconAd maxNumberOfAds overloads, and the SwipeableInterstitial preloader. Happy to split those into a separate commit if you'd prefer.

🤖 Generated with Claude Code

The Google Mobile Ads Next-Gen SDK binding
(Xamarin.Google.Android.Libraries.Ads.Mobile.Sdk) does not expose any usable
native ad API: the NativeAd and CustomNativeAd interfaces, MediaContent, and
NativeAdLoaderCallback.onNativeAdLoaded / onCustomNativeAdLoaded are all absent
from the generated assembly, so native ads cannot be loaded or displayed at all.

The root cause is MediaContent.a(), an obfuscated accessor returning
com.google.android.libraries.ads.mobile.sdk.internal.nativead.InternalNativeAd.
The internal package is removed by an existing remove-node, so this abstract
interface method has an unresolvable return type and MediaContent fails to
generate. That cascades: NativeAd.getMediaContent() and
CustomNativeAd.getMediaContent() then reference an unbound type, so both of
those interfaces are dropped too, which in turn silently drops the
onNativeAdLoaded / onCustomNativeAdLoaded callbacks and
NativeAdView.registerNativeAd.

Removing that internal accessor is the same approach already used for
BannerAd.getView()/a(AdSize) just above it.

Surfacing those types also revealed a generics-erasure conflict: NativeRequest
declares getCustomFormatIds() and getNativeAdTypes() with a raw java.util.List
return, while its implementors (NativeAdRequest, NativeSignalRequest) return the
reified List<String> / List<NativeAd.NativeAdType>. In C# that is IList versus
IList<T>, which cannot implement the interface member (CS0738), so the raw
interface members are removed as well - the same fix already applied to
BannerRequest.getAdSizes().

Verified by building the binding and running native ads end to end in a .NET
MAUI app on an Android emulator: NativeAdLoaderCallback.onNativeAdLoaded now
fires with a bound NativeAd, and NativeAdView.registerNativeAd renders it.

Note: regenerating PublicAPI.Unshipped.txt also picked up API that already
shipped in 1.2.1.1 but was not recorded in the file (AgeRestrictedTreatment,
the IconAd maxNumberOfAds overloads, and the SwipeableInterstitial preloader).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR restores the missing native-ad surface area in the ads-mobile-sdk binding by removing a problematic internal/obfuscated accessor that prevented MediaContent (and downstream native-ad types/callbacks) from generating, and by addressing a Java generics-erasure mismatch that caused interface implementation failures in C#.

Changes:

  • Remove MediaContent.a() (returns an internal type from a stripped package) so MediaContent, NativeAd, CustomNativeAd, and related callbacks/views bind successfully.
  • Remove NativeRequest.getCustomFormatIds() / getNativeAdTypes() raw-List members to avoid C# interface return-type mismatches (CS0738) with concrete implementors.
  • Update PublicAPI.Unshipped.txt and bump nugetVersion to 1.2.1.2.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated no comments.

File Description
source/com.google.android.libraries.ads.mobile.sdk/ads-mobile-sdk/Transforms/Metadata.xml Removes the internal/obfuscated accessor blocking native-ad bindings and drops raw List interface members that cause C# implementation conflicts.
source/com.google.android.libraries.ads.mobile.sdk/ads-mobile-sdk/PublicAPI/PublicAPI.Unshipped.txt Reflects the newly generated native-ad APIs (and other regenerated public API entries) after the binding fix.
config.json Bumps Xamarin.Google.Android.Libraries.Ads.Mobile.Sdk NuGet revision to 1.2.1.2 for the binding-only change.

@marius-bughiu

marius-bughiu commented Jul 24, 2026

Copy link
Copy Markdown
Contributor Author

👋 , I'm the maintainer of https://github.com/marius-bughiu/Plugin.AdMob and I'm trying to switch the plugin to the new version of the ads SDK, but some bindings are missing.

This PR has been tested using the sample app (and test ad units) and everything seems to work as expected. Below a few examples.

Regarding the nugetVersion bump - not sure if I'm supposed to do that or not, happy to revert if you want me to.

image image image

@jonathanpeppers

Copy link
Copy Markdown
Member

🤖 Reviewed this and it looks good to me. What I verified:

1. The fix matches existing precedent. The MediaContent.a() removal is the same pattern landed in #1356 (MediaContent.zza()/zzb() in play-services-ads-lite), and mirrors the BannerAd.getView() / BannerAd.a(AdSize) removals immediately above it in this same Metadata.xml. The NativeRequest.getCustomFormatIds()/getNativeAdTypes() raw-List removals mirror the existing BannerRequest.getAdSizes() removal.

2. No API breakage. Only one line is deleted from PublicAPI.Unshipped.txt (the IconAdRequest constructor, replaced by a longer overload on the next line). INativeRequest itself is newly surfaced by this PR, so dropping CustomFormatIds/NativeAdTypes from that interface breaks nothing — the reified generic properties still exist on NativeAdRequest/NativeSignalRequest.

3. The "already shipped but not recorded" API entries are legitimate — no need to split them out. I traced the drift: commit 6d0208c ("Stable Updates 20260615") bumped ads-mobile-sdk 1.1.1 → 1.2.0 and touched only config.json. Directory.Build.targets regenerates PublicAPI.Unshipped.txt post-build, but nothing in CI verifies it, so the file has been stale since that bump. AgeRestrictedTreatment, the IconAd maxNumberOfAds overloads and the SwipeableInterstitial preloader are real 1.2.x API catching up. Keeping them here is correct.

4. nugetVersion 1.2.1.1 → 1.2.1.2 is right — 1.2.1.1 is live on NuGet and the Maven version is unchanged.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants