How can I implement RewardedAdLoadCallback in Xamarin.Android?

Viewed 562

I'm trying to implement rewarded ads in Xamarin.Android in C#.

The code snippets in the Android documentation are written in Java and normally I don't have much problem adopting that code in C#, but now it does seem to be a problem.

To load a RewardedAd in Java, you have to do the following (according to the official documentation):

AdRequest adRequest = new AdRequest.Builder().build();

RewardedAd.load(this, "ca-app-pub-3940256099942544/5224354917",
      adRequest, new RewardedAdLoadCallback(){
        @Override
        public void onAdFailedToLoad(@NonNull LoadAdError loadAdError) {
          // Handle the error.
          Log.d(TAG, loadAdError.getMessage());
          mRewardedAd = null
        }

        @Override
        public void onAdLoaded(@NonNull RewardedAd rewardedAd) {
          mRewardedAd = rewardedAd;
          Log.d(TAG, "onAdFailedToLoad");
        }
    });

Now the problem here is that the object RewardedAdLoadCallback passed in this code, also defines the method implementations (onAdFailedToLoad and onAdLoaded), and in C# this cannot be done.

So my solution was to make my own class, MyRewardedAdLoadCallback, and override the methods.

public class MyRewardedAdLoadCallback : RewardedAdLoadCallback
{
    public override void OnAdLoaded(Java.Lang.Object p0)
    {
        base.OnAdLoaded(p0);
    }

    public override void OnAdFailedToLoad(LoadAdError p0)
    {
        base.OnAdFailedToLoad(p0);
    }
}

Then I simply load the rewarded ad with this code:

AdRequest adRequest = new AdRequest.Builder().Build();

RewardedAd.Load(this, "ca-app-pub-3940256099942544/5224354917", adRequest, new MyRewardedAdLoadCallback());

Now for some strange reason, I get this error when I build the project:

error: name clash: onAdLoaded(Object) in MyRewardedAdLoadCallback and onAdLoaded(AdT) in AdLoadCallback have the same erasure, yet neither overrides the other
    public void onAdLoaded (java.lang.Object p0)
  where AdT is a type-variable:
    AdT extends Object declared in class AdLoadCallback

I tried to search for this error on the internet and came to the conclusion that it is a Java error and has something to do with generic types (see this SO question). However I do not see any generic types in my code, nor in the Android documentation about rewarded ads. There is also zero Xamarin documentation about the rewarded ads I'm trying to implement.

I'd be really glad if someone could help me out with this. Thanks in advance.

3 Answers

Here's a hack to get both Interstitial and Rewarded ads working in Xamarin.GooglePlayServices.Ads version 120.0.0.

Add the following file to your project (requires building with the unsafe flag) and use the Android.Gms.Ads.Hack namespace to load your ads and subclass your callbacks.

For example, InterstitialAd.Load becomes global::Android.Gms.Ads.Hack.InterstitialAd.Load and InterstitialAdLoadCallback becomes global::Android.Gms.Ads.Hack.InterstitialAdLoadCallback.

The callback classes add OnInterstitialAdLoaded() and OnRewardedAdLoaded() virtual methods to the interstitial and rewarded ad load callbacks, respectively.

using System;
using System.ComponentModel;
using System.Diagnostics;
using Android.Content;
using Android.Runtime;
using Java.Interop;

namespace Android.Gms.Ads.Hack
{
    public abstract class InterstitialAd : Java.Lang.Object
    {
        private static readonly JniPeerMembers _members = new XAPeerMembers("com/google/android/gms/ads/interstitial/InterstitialAd", typeof(InterstitialAd));

        public unsafe static void Load(Context context, string adUnit, AdRequest request, AdLoadCallback callback)
        {
            IntPtr intPtr = JNIEnv.NewString(adUnit);
            try
            {
                JniArgumentValue* ptr = stackalloc JniArgumentValue[4];
                *ptr = new JniArgumentValue(context?.Handle ?? IntPtr.Zero);
                ptr[1] = new JniArgumentValue(intPtr);
                ptr[2] = new JniArgumentValue(request?.Handle ?? IntPtr.Zero);
                ptr[3] = new JniArgumentValue(callback?.Handle ?? IntPtr.Zero);
                _members.StaticMethods.InvokeVoidMethod("load.(Landroid/content/Context;Ljava/lang/String;Lcom/google/android/gms/ads/AdRequest;Lcom/google/android/gms/ads/interstitial/InterstitialAdLoadCallback;)V", ptr);
            }
            finally
            {
                JNIEnv.DeleteLocalRef(intPtr);
                GC.KeepAlive(context);
                GC.KeepAlive(request);
                GC.KeepAlive(callback);
            }
        }
    }

    [Register("com/google/android/gms/ads/interstitial/InterstitialAdLoadCallback", DoNotGenerateAcw = true)]
    public abstract class InterstitialAdLoadCallback : AdLoadCallback
    {
        private static readonly JniPeerMembers _members = new XAPeerMembers("com/google/android/gms/ads/interstitial/InterstitialAdLoadCallback", typeof(InterstitialAdLoadCallback));

        static IntPtr class_ref => _members.JniPeerType.PeerReference.Handle;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override JniPeerMembers JniPeerMembers => _members;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected override IntPtr ThresholdClass => _members.JniPeerType.PeerReference.Handle;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected override Type ThresholdType => _members.ManagedPeerType;

        protected InterstitialAdLoadCallback(IntPtr javaReference, JniHandleOwnership transfer)
            : base(javaReference, transfer)
        {
        }

        [Register(".ctor", "()V", "")]
        public unsafe InterstitialAdLoadCallback()
            : base(IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
        {
            if (!(base.Handle != IntPtr.Zero))
            {
                SetHandle(_members.InstanceMethods.StartCreateInstance("()V", GetType(), null).Handle, JniHandleOwnership.TransferLocalRef);
                _members.InstanceMethods.FinishCreateInstance("()V", this, null);
            }
        }

        private static Delegate cb_onAdLoaded;

        private static Delegate GetOnAdLoadedHandler()
        {
            if (cb_onAdLoaded is null)
            {
                cb_onAdLoaded = JNINativeWrapper.CreateDelegate((Action<IntPtr, IntPtr, IntPtr>)n_onAdLoaded);
            }

            return cb_onAdLoaded;
        }

        private static void n_onAdLoaded(IntPtr jnienv, IntPtr native__this, IntPtr native_p0)
        {
            InterstitialAdLoadCallback? @object = Java.Lang.Object.GetObject<InterstitialAdLoadCallback>(jnienv, native__this, JniHandleOwnership.DoNotTransfer);
            global::Android.Gms.Ads.Interstitial.InterstitialAd object2 = Java.Lang.Object.GetObject<global::Android.Gms.Ads.Interstitial.InterstitialAd>(native_p0, JniHandleOwnership.DoNotTransfer);
            @object!.OnInterstitialAdLoaded(object2);
        }

        [Register("onAdLoaded", "(Lcom/google/android/gms/ads/interstitial/InterstitialAd;)V", "GetOnAdLoadedHandler")]
        public virtual void OnInterstitialAdLoaded(global::Android.Gms.Ads.Interstitial.InterstitialAd interstitialAd)
        {
        }
    }

    public abstract class RewardedAd : Java.Lang.Object
    {
        private static readonly JniPeerMembers _members = new XAPeerMembers("com/google/android/gms/ads/rewarded/RewardedAd", typeof(RewardedAd));

        public unsafe static void Load(Context context, string adUnit, AdRequest request, RewardedAdLoadCallback callback)
        {
            IntPtr intPtr = JNIEnv.NewString(adUnit);
            try
            {
                JniArgumentValue* ptr = stackalloc JniArgumentValue[4];
                *ptr = new JniArgumentValue(context?.Handle ?? IntPtr.Zero);
                ptr[1] = new JniArgumentValue(intPtr);
                ptr[2] = new JniArgumentValue(request?.Handle ?? IntPtr.Zero);
                ptr[3] = new JniArgumentValue(callback?.Handle ?? IntPtr.Zero);
                _members.StaticMethods.InvokeVoidMethod("load.(Landroid/content/Context;Ljava/lang/String;Lcom/google/android/gms/ads/AdRequest;Lcom/google/android/gms/ads/rewarded/RewardedAdLoadCallback;)V", ptr);
            }
            finally
            {
                JNIEnv.DeleteLocalRef(intPtr);
                GC.KeepAlive(context);
                GC.KeepAlive(request);
                GC.KeepAlive(callback);
            }
        }
    }

    [Register("com/google/android/gms/ads/rewarded/RewardedAdLoadCallback", DoNotGenerateAcw = true)]
    public abstract class RewardedAdLoadCallback : AdLoadCallback
    {
        private static readonly JniPeerMembers _members = new XAPeerMembers("com/google/android/gms/ads/rewarded/RewardedAdLoadCallback", typeof(RewardedAdLoadCallback));

        static IntPtr class_ref => _members.JniPeerType.PeerReference.Handle;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override JniPeerMembers JniPeerMembers => _members;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected override IntPtr ThresholdClass => _members.JniPeerType.PeerReference.Handle;

        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected override Type ThresholdType => _members.ManagedPeerType;

        protected RewardedAdLoadCallback(IntPtr javaReference, JniHandleOwnership transfer)
            : base(javaReference, transfer)
        {
        }

        [Register(".ctor", "()V", "")]
        public unsafe RewardedAdLoadCallback()
            : base(IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
        {
            if (!(base.Handle != IntPtr.Zero))
            {
                SetHandle(_members.InstanceMethods.StartCreateInstance("()V", GetType(), null).Handle, JniHandleOwnership.TransferLocalRef);
                _members.InstanceMethods.FinishCreateInstance("()V", this, null);
            }
        }

        private static Delegate cb_onAdLoaded;

        private static Delegate GetOnAdLoadedHandler()
        {
            if (cb_onAdLoaded is null)
            {
                cb_onAdLoaded = JNINativeWrapper.CreateDelegate((Action<IntPtr, IntPtr, IntPtr>)n_onAdLoaded);
            }

            return cb_onAdLoaded;
        }

        private static void n_onAdLoaded(IntPtr jnienv, IntPtr native__this, IntPtr native_p0)
        {
            RewardedAdLoadCallback? @object = Java.Lang.Object.GetObject<RewardedAdLoadCallback>(jnienv, native__this, JniHandleOwnership.DoNotTransfer);
            global::Android.Gms.Ads.Rewarded.RewardedAd object2 = Java.Lang.Object.GetObject<global::Android.Gms.Ads.Rewarded.RewardedAd>(native_p0, JniHandleOwnership.DoNotTransfer);
            @object!.OnRewardedAdLoaded(object2);
        }

        [Register("onAdLoaded", "(Lcom/google/android/gms/ads/rewarded/RewardedAd;)V", "GetOnAdLoadedHandler")]
        public virtual void OnRewardedAdLoaded(global::Android.Gms.Ads.Rewarded.RewardedAd rewardedAd)
        {
        }
    }
}
public abstract class InterstitialCallback : Android.Gms.Ads.Interstitial.InterstitialAdLoadCallback
{
    [Register("onAdLoaded", "(Lcom/google/android/gms/ads/interstitial/InterstitialAd;)V", "GetOnAdLoadedHandler")]
    public virtual void OnAdLoaded(Android.Gms.Ads.Interstitial.InterstitialAd interstitialAd)
    {
    }

    private static Delegate cb_onAdLoaded;
    private static Delegate GetOnAdLoadedHandler()
    {
        if (cb_onAdLoaded is null)
            cb_onAdLoaded = JNINativeWrapper.CreateDelegate((Action<IntPtr, IntPtr, IntPtr>)n_onAdLoaded);
        return cb_onAdLoaded;
    }
    private static void n_onAdLoaded(IntPtr jnienv, IntPtr native__this, IntPtr native_p0)
    {
        InterstitialCallback thisobject = GetObject<InterstitialCallback>(jnienv, native__this, JniHandleOwnership.DoNotTransfer);
        Android.Gms.Ads.Interstitial.InterstitialAd resultobject = GetObject<Android.Gms.Ads.Interstitial.InterstitialAd>(native_p0, JniHandleOwnership.DoNotTransfer);
        thisobject.OnAdLoaded(resultobject);
    }
}
public abstract class RewardedAdLoadCallback : Android.Gms.Ads.Rewarded.RewardedAdLoadCallback
{
    [Register("onAdLoaded", "(Lcom/google/android/gms/ads/rewarded/RewardedAd;)V", "GetOnAdLoadedHandler")]
    public virtual void OnAdLoaded(Android.Gms.Ads.Rewarded.RewardedAd rewardedAd)
    {
    }

    private static Delegate cb_onAdLoaded;
    private static Delegate GetOnAdLoadedHandler()
    {
        if (cb_onAdLoaded is null)
        {
            cb_onAdLoaded = JNINativeWrapper.CreateDelegate((Action<IntPtr, IntPtr, IntPtr>)n_onAdLoaded);
        }

        return cb_onAdLoaded;
    }
    private static void n_onAdLoaded(IntPtr jnienv, IntPtr native__this, IntPtr native_p0)
    {
        RewardedAdLoadCallback thisobject = GetObject<RewardedAdLoadCallback>(jnienv, native__this, JniHandleOwnership.DoNotTransfer);
        Android.Gms.Ads.Rewarded.RewardedAd resultobject = GetObject<Android.Gms.Ads.Rewarded.RewardedAd>(native_p0, JniHandleOwnership.DoNotTransfer);
        thisobject.OnAdLoaded(resultobject);
    }
}

Maybe just subclass the callback

class RewardedVideoAds : RewardedAdLoadCallback
    {
        private RewardedAd rewardedAd;

        
        public RewardedVideoAds()
        {
            
            AdRequest adRequest = new AdRequest.Builder().Build();
           
            RewardedAd.Load(MainActivity.Current, "ca-app-pub-3940256099942544/5224354917", adRequest, this);
        }
        public override void OnAdFailedToLoad(LoadAdError p0)
        {
            base.OnAdFailedToLoad(p0);
        }
        public override void OnAdLoaded(Java.Lang.Object p0)
        {
            base.OnAdLoaded(p0);
        }
    }
Related