Native Custom Events

This guide is intended for publishers who want to use AdMob Mediation to load and display native ads from a network that isn't one of AdMob's supported ad networks. (See Custom Events for information about using AdMob Mediation to load and display banner, interstitial, or rewarded display ads.) Custom events are mediation adapter classes capable of requesting ads from another network. When you add the name of one of these classes to the mediation settings for an ad unit, the SDK can instantiate and use it to retrieve ads. Native custom event classes need to be able to do the following:

  • Request native ads from a mediated network.
  • Forward events from the mediated network's SDK to the Google Mobile Ads SDK.
  • Use a NativeAdMapper to map mediated native ads to AdMob's native ad interface.

Each of these tasks is covered below. You can also find the full source for a sample custom event project in our GitHub repository.

Prerequisites

To implement an AdMob custom event, you must first integrate the Mobile Ads SDK into your project. You may also want to first read about making an AdRequest and how mediation works.

Sample SDK

The code snippets in this guide come from our sample custom event project which includes a "Sample SDK". This mock SDK is used in our example of how to build a custom event that mediates another ad network's SDK.

Sample SDK features classes similar to what you'd find in an ad network's production SDK. There are request objects like SampleNativeAdRequest, ad loaders like SampleNativeAdLoader, and other classes, constants, and interfaces used to simulate a real network's SDK. The ads that it produces are mocks, though, and no additional network traffic is generated.

Request a native ad

The requestNativeAd() method

Custom event classes must implement the CustomEventNative interface, which includes a method used by the Google Mobile Ads SDK to request native ads from the custom event:

Java

void requestNativeAd(Context context,
        CustomEventNativeListener listener,
        String serverParameter,
        NativeMediationAdRequest mediationAdRequest,
        Bundle customEventExtras);

Kotlin

override fun requestNativeAd(context: Context,
        listener: CustomEventNativeListener,
        serverParameter: String,
        mediationAdRequest: NativeMediationAdRequest,
        customEventExtras: Bundle?)

When this method is called, the custom event should asynchronously request a native ad from the mediated network. The following parameters for requestNativeAd() carry information that the custom event can use in making its request:

  • serverParameter - When adding a custom event to an ad unit's mediation configuration, publishers can enter a string value to be passed along with each request. This parameter holds that value (typically another ad unit ID, which was issued by the mediated network).
  • mediationAdRequest - A NativeMediationAdRequest object, which contains properties specific to a single request, such as which native formats are requested. NativeMediationAdRequest is a subclass of MediationAdRequest, so it also includes properties for other targeting information provided by publishers at request time.
  • customEventExtras - A Bundle containing any "extra" information relevant to the request provided by the publisher. When creating an AdRequest, publishers can use the addNetworkExtrasBundle() method to include information for specific mediation adapters and custom events. Any extras included by the publisher for a given custom event class are found in this parameter.

In addition to the parameters carrying request information, there are two others:

  • context - A Context object which can be used if the custom event requires one.
  • listener - A CustomEventNativeListener object that the custom event should use to forward events.

The listener object is particularly important, as it's used to report events to the Google Mobile Ads SDK. This guide covers how to do so in a later section.

Here's a code snippet from our example custom event project that shows an implemented requestNativeAd() method:

SampleCustomEvent (excerpt)

Java

@Override
public void requestNativeAd(Context context,
        CustomEventNativeListener customEventNativeListener,
        String serverParameter,
        NativeMediationAdRequest nativeMediationAdRequest,
        Bundle extras) {
    SampleNativeAdLoader loader = new SampleNativeAdLoader(context);
    loader.setAdUnit(serverParameter);

    SampleNativeAdRequest request = new SampleNativeAdRequest();
    NativeAdOptions options = nativeMediationAdRequest.getNativeAdOptions();

    if (options != null) {
        request.setShouldDownloadImages(options.shouldReturnUrlsForImageAssets());
    } else {
        // Set a default value for the one native ad option the Sample SDK supports.
        request.setShouldDownloadImages(true);
    }

    request.setAppInstallAdsRequested(nativeMediationAdRequest.isAppInstallAdRequested());
    request.setContentAdsRequested(nativeMediationAdRequest.isContentAdRequested());

    loader.setNativeAdListener(
            new SampleCustomNativeEventForwarder(customEventNativeListener, options));

    loader.fetchAd(request);
}

Kotlin

override fun requestNativeAd(context: Context,
                    customEventNativeListener: CustomEventNativeListener,
                    serverParameter: String,
                    nativeMediationAdRequest: NativeMediationAdRequest,
                    extras: Bundle?) {
    val loader = SampleNativeAdLoader(context)
    loader.setAdUnit(serverParameter)

    val request = SampleNativeAdRequest()
    val options = nativeMediationAdRequest.nativeAdOptions

    request.setShouldDownloadImages(options?.shouldReturnUrlsForImageAssets() ?: true)

    request.setAppInstallAdsRequested(nativeMediationAdRequest.isAppInstallAdRequested)
    request.setContentAdsRequested(nativeMediationAdRequest.isContentAdRequested)

    loader.setNativeAdListener(
            SampleCustomNativeEventForwarder(customEventNativeListener, options))

    loader.fetchAd(request)
}

This is a sample custom event that mediates to an imaginary ad network via the mock Sample SDK. The custom event uses the information provided in the parameters of requestNativeAd() to build a SampleNativeAdRequest (a class provided by Sample SDK), then uses it to request an ad from Sample SDK's SampleNativeAdLoader. It also creates a SampleCustomEventNativeForwarder to handle forwarding events back to the Google Mobile Ads SDK. That process is the topic of the next section.

Native ad formats

As noted above, the NativeMediationAdRequest object includes information about which native ad formats the publisher is requesting. It's important that custom events respect this choice and return only those formats that the publisher indicated they're prepared to display. Otherwise, they could cause the publisher's rendering code to fail.

For example, if the mediated ad network only ever provides app install ads and the publisher requests content ads alone, your custom event should not request an app install ad anyway and then try to map it to the content ad format. Instead, it should call the onAdFailedToLoad() method on the CustomEventNativeListener object with an error code of AdRequest.ERROR_CODE_INVALID_REQUEST.

NativeAdOptions

When making a request for native ads, publishers use a NativeAdOptions object to specify their preferences for that request, such as how image assets should be returned. Your custom event needs to examine those preferences and respect them. Custom events can retrieve a request's NativeAdOptions object using the getNativeAdOptions() method provided by the NativeMediationAdRequest object:

Java

NativeAdOptions options = mediationAdRequest.getNativeAdOptions();

Kotlin

val options = mediationAdRequest.nativeAdOptions

After retrieving the NativeAdOptions, custom events can read its properties and act accordingly. For example, if the shouldReturnUrlsForImageAssets value is false in NativeAdOptions (which is the default), the custom event must return actual image assets rather than just the URLs. In this case, if the SDK being mediated only provides URLs for images, then the custom event must use those URLs to download the image files and make their data available to the publisher in its mapped native ads.

Forward events to the Mobile Ads SDK

A few things can happen when a custom event tries to load a native ad from its mediated network. The SDK could successfully return a native ad, it could come back with an error, or it might simply report that no ads are available. Similarly, when the user taps on an ad, the mediated SDK might open an overlay or leave the application entirely to start an external browser. It's important for the Google Mobile Ads SDK to be aware of these events, so it provides a CustomEventNativeListener object as a parameter to the requestNativeAd() method.

It's part of a custom event's job to act as a "man-in-the-middle," listening for events from the mediated SDK, and then calling the corresponding CustomEventNativeListener() method as appropriate. Your custom event needs to be aware of the following methods :

  • onAdLoaded() - This method should be called when a custom event successfully loads a native ad. It takes a single parameter of type NativeAdMapper, which the custom event should use to pass a mapped version of the native ad it just loaded (NativeAdMapper classes are covered in the next section).
  • onAdFailedToLoad() - When the custom event tries and fails to load a native ad, it should use this method to report the failure. It takes a single integer parameter, which should be set to one of the error constants: ERROR_CODE_INTERNAL_ERROR, ERROR_CODE_INVALID_REQUEST, ERROR_CODE_NETWORK_ERROR, or ERROR_CODE_NO_FILL.
  • onAdLeftApplication() - Invoke this method when the mediated SDK causes user focus to leave the publisher's application (most commonly to open an external browser).
  • onAdOpened() - If the native ad opens any overlay or separate activity that covers the interface in response to a user's tap, this method should be invoked. This includes an external browser, in which case your custom event should call onAdOpened just before onAdLeftApplication.
  • onAdClosed() - If an overlay or separate activity covering the interface is closed, this method should be invoked, signifying that control is being transferred back to the application containing the native ad.

The listener also offers methods for reporting clicks and impressions from custom events that choose to track clicks or impressions for themselves (see Override the default click handling and impression recording for details). One is for reporting a click to the Google Mobile Ads SDK, and one is for reporting an impression:

  • onAdClicked() - This method should be called when a user clicks on a native ad.
  • onAdImpression() - Similarly, this method should be called when an impression is recorded for the ad by the mediated SDK.

There are a number of ways to make sure events are forwarded correctly, but one of the simplest is to create a dedicated class to act as forwarder. Here's one from our custom event sample project:

SampleCustomNativeEventForwarder

Java

public class SampleCustomNativeEventForwarder extends SampleNativeAdListener {
    private CustomEventNativeListener mNativeListener;
    private NativeAdOptions mNativeAdOptions;

    public SampleCustomNativeEventForwarder(
            CustomEventNativeListener listener, NativeAdOptions adOptions) {
        this.mNativeListener = listener;
        this.mNativeAdOptions = adOptions;
    }

    @Override
    public void onNativeAppInstallAdFetched(SampleNativeAppInstallAd ad) {
        SampleNativeAppInstallAdMapper mapper = new SampleNativeAppInstallAdMapper(ad, mNativeAdOptions);
        mNativeListener.onAdLoaded(mapper);
    }

    @Override
    public void onNativeContentAdFetched(SampleNativeContentAd ad) {
        SampleNativeContentAdMapper mapper = new SampleNativeContentAdMapper(ad, mNativeAdOptions);
        mNativeListener.onAdLoaded(mapper);
    }

    @Override
    public void onAdFetchFailed(SampleErrorCode errorCode) {
        switch (errorCode) {
            case UNKNOWN:
                mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_INTERNAL_ERROR);
                break;
            case BAD_REQUEST:
                mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_INVALID_REQUEST);
                break;
            case NETWORK_ERROR:
                mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_NETWORK_ERROR);
                break;
            case NO_INVENTORY:
                mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_NO_FILL);
                break;
        }
    }
}

Kotlin

class SampleCustomNativeEventForwarder(
        private val mNativeListener: CustomEventNativeListener,
        private val mNativeAdOptions: NativeAdOptions) : SampleNativeAdListener() {

    fun onNativeAppInstallAdFetched(ad: SampleNativeAppInstallAd) {
        val mapper = SampleNativeAppInstallAdMapper(ad, mNativeAdOptions)
        mNativeListener.onAdLoaded(mapper)
    }

    fun onNativeContentAdFetched(ad: SampleNativeContentAd) {
        val mapper = SampleNativeContentAdMapper(ad, mNativeAdOptions)
        mNativeListener.onAdLoaded(mapper)
    }

    fun onAdFetchFailed(errorCode: SampleErrorCode) {
        when (errorCode) {
            UNKNOWN -> mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_INTERNAL_ERROR)
            BAD_REQUEST -> mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_INVALID_REQUEST)
            NETWORK_ERROR -> mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_NETWORK_ERROR)
            NO_INVENTORY -> mNativeListener.onAdFailedToLoad(AdRequest.ERROR_CODE_NO_FILL)
        }
    }
}

Notice that the class implements Sample SDK's SampleNativeAdListener interface. In SampleCustomEvent (excerpt) an instance of this forwarder class was given to the SampleNativeAdLoader in its setNativeAdListener() method:

Java

loader.setNativeAdListener(new SampleCustomNativeEventForwarder(customEventNativeListener, options));

Kotlin

loader.setNativeAdListener(SampleCustomNativeEventForwarder(customEventNativeListener, options))

This ensures that Sample SDK calls the SampleCustomNativeEventForwarder object's listener methods (onNativeAppInstallAdFetched(), onAdFetchFailed(), and so on) when it has an event to report. The forwarder then calls the appropriate method on the Google Mobile Ads SDK's CustomEventNativeListener, forwarding the event. In short, the Google Mobile Ads SDK listens to the forwarder, which is listening to the mediated SDK.

The onAdFetchFailed() method above is a great example of how this works. When Sample SDK fails to load an ad, it calls the forwarder's onAdFetchFailed() method and gives it an error code. The forwarder examines the error code and calls the onAdFailedToLoad() method of the CustomEventNativeListener with one of the error codes used by the Google Mobile Ads SDK. In this way, a Sample SDK event is turned into a Google Mobile Ads SDK event.

Map native ads

Different SDKs have their own unique formats for native ads. One might return objects that contain a "title" field, for instance, while another might have "headline." Additionally, the methods used to track impressions and process clicks may vary from one SDK to another. It's the job of the NativeAdMapper to smooth out these wrinkles and adapt a mediated SDK's native ad object to match the interface expected by the Google Mobile Ads SDK.

Each of AdMob's system-defined native ad formats has a corresponding NativeAdMapper class: NativeAppInstallAdMapper maps app install ads, and NativeContentAdMapper maps content ads. Custom events should subclass these to create their own mappers specific to their mediated SDK.

Here's a sample app install ad mapper from our example custom event project:

SampleNativeAppInstallAdMapper

Java

public class SampleNativeAppInstallAdMapper extends NativeAppInstallAdMapper {

    private final SampleNativeAppInstallAd mSampleAd;
    private NativeAdOptions mNativeAdOptions;
    private ImageView mInformationIconView;

    public SampleNativeAppInstallAdMapper(SampleNativeAppInstallAd ad, NativeAdOptions adOptions) {
        mSampleAd = ad;
        mNativeAdOptions = adOptions;
        setHeadline(mSampleAd.getHeadline());
        setBody(mSampleAd.getBody());
        setCallToAction(mSampleAd.getCallToAction());
        setStarRating(mSampleAd.getStarRating());
        setStore(mSampleAd.getStoreName());
        setIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(),
                SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));

        List imagesList = new ArrayList();
        imagesList.add(new SampleNativeMappedImage(ad.getImage(), ad.getImageUri(),
                SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));
        setImages(imagesList);

        NumberFormat formatter = NumberFormat.getCurrencyInstance();
        String priceString = formatter.format(mSampleAd.getPrice());
        setPrice(priceString);

        Bundle extras = new Bundle();
        extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());
        this.setExtras(extras);

        setOverrideClickHandling(false);
        setOverrideImpressionRecording(false);
    }

    @Override
    public void recordImpression() {
        mSampleAd.recordImpression();
    }

    @Override
    public void handleClick(View view) {
        mSampleAd.handleClick(view);
    }
}

Kotlin

class SampleNativeAppInstallAdMapper(private val mSampleAd: SampleNativeAppInstallAd,
        private val mNativeAdOptions: NativeAdOptions) : NativeAppInstallAdMapper() {
    private val mInformationIconView: ImageView? = null

    init {
        headline = mSampleAd.headline
        body = mSampleAd.body
        callToAction = mSampleAd.callToAction
        starRating = mSampleAd.starRating
        store = mSampleAd.storeName
        icon = SampleNativeMappedImage(mSampleAd.appIcon, mSampleAd.appIconUri,
                SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE)

        val imagesList = ArrayList()
        imagesList.add(SampleNativeMappedImage(mSampleAd.image, mSampleAd.imageUri,
                SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE))
        images = imagesList

        val formatter = NumberFormat.getCurrencyInstance()
        val priceString = formatter.format(mSampleAd.price)
        price = priceString

        val extras = Bundle()
        extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, mSampleAd.degreeOfAwesomeness)
        this.extras = extras

        overrideClickHandling = false
        overrideImpressionRecording = false
    }

    override fun recordImpression() {
        mSampleAd.recordImpression()
    }

    override fun handleClick(view: View) {
        mSampleAd.handleClick(view)
    }
}

Let's take a look at the constructor method and some of the work it's doing:

Hold a reference to the mediated native ad object

The constructor accepts SampleNativeAppInstallAd and NativeAdOptions parameters. SampleNativeAppInstallAd is the native ad class used by the Sample SDK for its app install ads. The mapper needs a reference to the mediated ad so it can pass on click and impression events. NativeAdOptions contains publisher preferences about the native ad. Both parameters are stored as local variables.

Set mapped asset properties

This mapper class inherits some app install-specific asset properties from its parent, NativeAppInstallAdMapper. The constructor retrieves asset data from the mediated ad and uses it to populate those properties. These lines, for example, get the mediated ad's price data and uses it to set the mapper's property of the same name:

Java

NumberFormat formatter = NumberFormat.getCurrencyInstance();
String priceString = formatter.format(mSampleAd.getPrice());
setPrice(priceString);

Kotlin

val formatter = NumberFormat.getCurrencyInstance()
val priceString = formatter.format(mSampleAd.price)
price = priceString

In this case, the mediated ad stores the price as a double, while AdMob uses a String for the same asset. The mapper is responsible for handling these types of conversions.

Map image assets

Mapping image assets is more complicated than simpler data types like double or String. Images might be downloaded automatically or simply returned as URL values. Their pixel-to-dpi scales can also vary. To help custom event developers manage these details, the Google Mobile Ads SDK provides the NativeAd.Image class. In much the same way that developers need to create a subclass of NativeAdMapper to map a mediated native ad, they should also create a subclass of NativeAd.Image to help them map its image assets.

The SampleNativeAppInstallAdMapper uses its mapped image class in this line to set the mapper's icon image asset:

Java

setIcon(new SampleNativeMappedImage(ad.getAppIcon(), ad.getAppIconUri(),
        SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE));

Kotlin

icon = SampleNativeMappedImage(mSampleAd.appIcon, mSampleAd.appIconUri,
        SampleCustomEvent.SAMPLE_SDK_IMAGE_SCALE)

Here's the code for the custom event's SampleNativeMappedImage class:

Java

public class SampleNativeMappedImage extends NativeAd.Image {

    private Drawable mDrawable;
    private Uri mImageUri;
    private double mScale;

    public SampleNativeMappedImage(Drawable drawable, Uri imageUri, double scale) {
        mDrawable = drawable;
        mImageUri = imageUri;
        mScale = scale;
    }

    @Override
    public Drawable getDrawable() {
        return mDrawable;
    }

    @Override
    public Uri getUri() {
        return mImageUri;
    }

    @Override
    public double getScale() {
        return mScale;
    }
}

Kotlin

class SampleNativeMappedImage(private val mDrawable: Drawable,
                              private val mImageUri: Uri,
                              private val mScale: Double) : NativeAd.Image() {

    override fun getDrawable(): Drawable {
        return mDrawable
    }

    override fun getUri(): Uri {
        return mImageUri
    }

    override fun getScale(): Double {
        return mScale
    }
}

In this case, the mapped image class merely provides a convenient structure for holding the image and its metadata. Your custom event's mapped image classes can, however, include other code necessary for the conversion.

Add additional fields to the extras Bundle

Some mediated SDKs may provide additional assets beyond those in the AdMob native ad formats. The NativeAdMapper classes include a Bundle property called extras that is used to pass these assets on to publishers. The mapper above makes use of this for Sample SDK's "degree of awesomeness" asset:

Java

Bundle extras = new Bundle();
extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, ad.getDegreeOfAwesomeness());
this.setExtras(extras);

Kotlin

val extras = Bundle()
extras.putString(SampleCustomEvent.DEGREE_OF_AWESOMENESS, mSampleAd.degreeOfAwesomeness)
this.extras = extras

Bundles are key-value data structures, so multiple assets should be included in a single bundle with distinct keys. Publishers can retrieve the data using the NativeAd class's getExtras() method.

AdChoices

Your adapter is responsible for placing your AdChoices icon in Google's native ad view. You should use the trackView and untrackView APIs provided in NativeAppInstallAdMapper and NativeContentAdMapper to add and remove your AdChoices view.

The adapter must also respect the publisher's preference of AdChoices location by checking at the getAdChoicesPlacement() method in NativeAdOptions and rendering the ad in the publisher's preferred corner. If a preferred corner is not set, AdChoices should be rendered in the top right corner.

Here's a snippet from SampleNativeAppInstallAdMapper showing how to respect the publisher's AdChoices placement and render the AdChoices icon:

Java

@Override
public void trackView(View view) {
    super.trackView(view);

    if (!(view instanceof ViewGroup)) {
        Log.w(SampleCustomEvent.TAG, "Failed to show information icon. Ad view not a ViewGroup.");
        return;
    }

    ViewGroup adView = (ViewGroup) view;
    // Find the overlay view in the given ad view. The overlay view will always be the
    // top most view in the hierarchy.
    View overlayView = adView.getChildAt(adView.getChildCount() - 1);

    if (overlayView instanceof FrameLayout) {
        // The sample SDK returns a view for AdChoices asset. If your SDK provides image and
        // click through URLs instead of the view asset, the adapter is responsible for
        // downloading the icon image and creating the AdChoices icon view.

        // Get the information icon provided by the Sample SDK.
        mInformationIconView = mSampleAd.getInformationIcon();

        // Add the view to the overlay view.
        ((ViewGroup) overlayView).addView(mInformationIconView);

        // We know that the overlay view is a FrameLayout, so we get the FrameLayout's
        // LayoutParams from the AdChoicesView.
        FrameLayout.LayoutParams params =
                (FrameLayout.LayoutParams) mInformationIconView.getLayoutParams();

        if (mNativeAdOptions == null) {
            // Default to top right if native ad options are not provided.
            params.gravity = Gravity.TOP | Gravity.RIGHT;
        } else {
            switch (mNativeAdOptions.getAdChoicesPlacement()) {
                case NativeAdOptions.ADCHOICES_TOP_LEFT:
                    params.gravity = Gravity.TOP | Gravity.LEFT;
                    break;
                case NativeAdOptions.ADCHOICES_BOTTOM_RIGHT:
                    params.gravity = Gravity.BOTTOM | Gravity.RIGHT;
                    break;
                case NativeAdOptions.ADCHOICES_BOTTOM_LEFT:
                    params.gravity = Gravity.BOTTOM | Gravity.LEFT;
                    break;
                case NativeAdOptions.ADCHOICES_TOP_RIGHT:
                default:
                    params.gravity = Gravity.TOP | Gravity.RIGHT;
            }
        }
        adView.requestLayout();
    } else {
        Log.w(SampleCustomEvent.TAG, "Failed to show information icon. Overlay view not found.");
    }
}

@Override
public void untrackView(View view) {
    super.untrackView(view);

    // Remove the previously added AdChoices view from the ad view.
    ViewParent parent = mInformationIconView.getParent();
    if (parent != null && parent instanceof ViewGroup) {
        ((ViewGroup) parent).removeView(mInformationIconView);
    }
}

Kotlin

override fun trackView(view: View) {
    super.trackView(view)

    if (view !is ViewGroup) {
        Log.w(SampleCustomEvent.TAG, "Failed to show information icon. Ad view not a ViewGroup.")
        return
    }

    // Find the overlay view in the given ad view. The overlay view will always be the
    // top most view in the hierarchy.
    val overlayView = view.getChildAt(view.childCount - 1)

    if (overlayView is FrameLayout) {
        // The sample SDK returns a view for AdChoices asset. If your SDK provides image and
        // click through URLs instead of the view asset, the adapter is responsible for
        // downloading the icon image and creating the AdChoices icon view.

        // Get the information icon provided by the Sample SDK.
        mInformationIconView = mSampleAd.informationIcon()

        // Add the view to the overlay view.
        (overlayView as ViewGroup).addView(mInformationIconView)

        // We know that the overlay view is a FrameLayout, so we get the FrameLayout's
        // LayoutParams from the AdChoicesView.
        val params = mInformationIconView.layoutParams as FrameLayout.LayoutParams

        if (mNativeAdOptions == null) {
            // Default to top right if native ad options are not provided.
            params.gravity = Gravity.TOP or Gravity.RIGHT
        } else {
            params.gravity = when (mNativeAdOptions.adChoicesPlacement) {
                NativeAdOptions.ADCHOICES_TOP_LEFT -> Gravity.TOP or Gravity.LEFT
                NativeAdOptions.ADCHOICES_BOTTOM_RIGHT -> Gravity.BOTTOM or Gravity.RIGHT
                NativeAdOptions.ADCHOICES_BOTTOM_LEFT -> Gravity.BOTTOM or Gravity.LEFT
                else -> Gravity.TOP or Gravity.RIGHT
            }
        }
        view.requestLayout()
    } else {
        Log.w(SampleCustomEvent.TAG, "Failed to show information icon. Overlay view not found.")
    }
}

override fun untrackView(view: View) {
    super.untrackView(view)

    // Remove the previously added AdChoices view from the ad view.
    val parent = mInformationIconView.parent
    if (parent != null && parent is ViewGroup) {
        parent.removeView(mInformationIconView)
    }
}

Impression and click events

It's important that the mediated SDK be notified any time an impression or click occurs, so the Google Mobile Ads SDK helps custom event developers report these events. There are two different approaches custom events can use, depending on what the mediated network's SDK requires.

Handle clicks and impressions with handleClick and recordImpression

If the mediated native ad objects provide methods to record clicks and impressions, a custom event's mapper classes can use them. This is the most common approach. The NativeAdMapper includes two methods, recordImpression() and handleClick(), which custom events should override and use to call the corresponding method on the mediated native ad object.

Here's how the SampleNativeAppInstallAdMapper handles this:

Java

@Override
public void recordImpression() {
    mSampleAd.recordImpression();
}

@Override
public void handleClick(View view) {
    mSampleAd.handleClick(view);
}

Kotlin

override fun recordImpression() {
    mSampleAd.recordImpression()
}

override fun handleClick(view: View) {
    mSampleAd.handleClick(view)
}

Because the SampleNativeAppInstallAdMapper holds a reference to the Sample SDK's native ad object, it can simply call the appropriate method on that object to report a click or impression. Note that the handleClick() method has a single parameter, which is the View object corresponding to the native ad asset that received the click.

Override the default click handling and impression recording

Some mediated SDKs may prefer to track clicks and impressions on their own. In that case, you should override the default click and impression tracking by making the following two calls in the constructor of your NativeAdMapper:

Java

setOverrideClickHandling(true);
setOverrideImpressionRecording(true);

Kotlin

setOverrideClickHandling(true)
setOverrideImpressionRecording(true)

As noted in Forward events to the Mobile Ads SDK, custom events that track clicks and impressions for themselves are required to report them to the Google Mobile Ads SDK via the onAdClicked() and onAdImpression() methods.)

You should also override the trackView() method and use it to pass the native ad's view to the mediated SDK's native ad object, so the mediated SDK can use it for tracking purposes. The sample SDK from our custom event example project (from which this guide's code snippets have been taken) doesn't use this approach, but if it did, the custom event code would look like this:

Java

public void trackView(View view) {
    mSampleAd.setNativeAdViewForTracking(view);
}

Kotlin

override fun trackView(View view) {
    mSampleAd.setNativeAdViewForTracking(view)
}

The NativeAdMapper offers a corresponding untrackView() method that custom events should override for the opposite purpose (releasing any references to the view and disassociating it from the native ad object).

Use a custom event

To use a custom event, you need to add it to the mediation configuration for an ad unit. This is done in the AdMob interface. (Create a custom event provides detailed instructions for editing an ad unit's mediation configuration).

When you add the custom event to an ad unit's mediation configuration, you are asked for three pieces of information:

  • Class Name - This is the class name of your custom event, with the full package name included.
  • Label - This is the label you'd like AdMob's interface to use to represent the custom event when it displays the ad unit's mediation sources. It's just for you, as it only ever appears on AdMob.com.
  • Parameter - This is a string value that is passed to the custom event whenever requests for this ad unit are made. Typically this value is set to an ad unit ID from the mediated network.

Here's a screenshot of an example custom event entry:

That's it! You now have everything you need to write your own Android custom events for AdMob.

Send feedback about...

AdMob for Android
AdMob for Android
Need help? Visit our support page.