Native Ads - cleveradssolutions/CAS-Android GitHub Wiki

Native ads are advertising assets seamlessly integrated into the user interface using components that are native to the platform. They are displayed through the same views you use to construct your app's layouts, allowing for a cohesive user experience. Additionally, these ads can be customized to align with your app's visual design, ensuring they feel like a natural part of the content rather than intrusive advertisements.

Load an Ad

Native ads are loaded with the CASNativeLoader class. A NativeAdContentCallback class for handling events related to native ad content.
In most cases, a casId is the same as your application package name.

val nativeAdCallback = object : NativeAdContentCallback() {  
    override fun onNativeAdLoaded(nativeAd: NativeAdContent, ad: AdContent) {  
        keepNativeAdInMemory(nativeAd)
        displayNativeAd(nativeAd)    
    }  
    override fun onNativeAdFailedToLoad(error: AdError) {  
        // (Optional) Handle Ad load errors  
    }  
    override fun onNativeAdFailedToShow(nativeAd: NativeAdContent, error: AdError) {  
        // (Optional) Handle Ad render errors. 
        // Called from CASNativeView.setNativeAd(nativeAd)  
    }  
    override fun onNativeAdClicked(nativeAd: NativeAdContent, ad: AdContent) {  
        // (Optional) Called when the native ad is clicked by the user.  
    }  
}

fun loadNativeAd() {
    val casId = BuildConfig.APPLICATION_ID
    val adLoader = CASNativeLoader(context, casId, callback)
    adLoader.adChoicesPlacement = AdChoicesPlacement.TOP_LEFT
    adLoader.isStartVideoMuted = true
    adLoader.onImpressionListener = impressionListener // optional
    
    adLoader.load()
}

fun keepNativeAdInMemory(nativeAd: NativeAdContent) {
    loadedNativeAds.add(nativeAd)
}

After a call to load(), a single callback is made to the previously defined NativeAdContentCallback to deliver the native ad object or report an error.

Note

A single NativeAdContent can be displayed only once. Attempting to display the ad that has already been displayed will result in the onNativeAdFailedToShow callback.

Load multiple ads (optional)

The load() method takes an additional parameter: the number of ads the SDK should attempt to load for the request. It's not guaranteed that the SDK will return the exact number of ads requested.

val maxNumberOfAdsToLoad = 3
adLoader.load(maxNumberOfAdsToLoad)

The onNativeAdLoaded will be called multiple times, once for each ad that is successfully loaded, up to the specified maximum number of ads.
If the load operation fails, the onNativeAdFailedToLoad will be called once with the error details.

Apps requesting multiple ads should call CASNativeLoader.isLoading in their callback implementations to determine whether the loading process has finished.

    override fun onNativeAdLoaded(nativeAd: NativeAdContent, ad: AdContent) {    
        keepNativeAdInMemory(nativeAd)
        displayNativeAd(nativeAd)
        
        if (adLoader.isLoading) {
            // The AdLoader is still loading ads.
            // Expect more onNativeAdLoaded or onNativeAdFailedToLoad callbacks.
        } else {
            // The AdLoader has finished loading ads.
        }   
    } 

Validate Ad Content (Optional)

Native ads can be preloaded and cached to reduce latency when displaying ads to users. However, these ads have a limited lifespan and may expire after a certain period. To ensure a seamless user experience, you should validate the ad content before displaying it.

Use the NativeAdContent.isExpired property to check if the ad has expired. If you attempt to display an expired ad, it will trigger an onNativeAdFailedToShow callback.

override fun onNativeAdLoaded(nativeAd: NativeAdContent, ad: AdContent) {    
    keepNativeAdInMemory(nativeAd)
    // Do not display the ad here;
    // instead, call tryDisplayNativeAdAtIndex() to display it later
}
...

fun tryDisplayNativeAdAtIndex(index: Int): Boolean {
    loadedNativeAds.getOrNull(index)?.let { nativeAd ->
        if (!nativeAd.isExpired) {
            displayNativeAd(nativeAd)
            return true
        }
    }
    return false
}

If you display the ad immediately in onNativeAdLoaded, there's no need to check its validity, as it is guaranteed to be fresh at that moment.

Release Resources

It’s crucial to call the destroy() method on all loaded native ads, even if they were not used or referenced. This action releases utilized resources and helps prevent memory leaks.

When you invoke the load() function, the CASNativeLoader will continue running until the ad finishes loading. This can lead to the NativeAdContentCallback being triggered after the Activity has been destroyed or at unexpected times. Therefore, you should implement checks to destroy the loaded ad if you do not plan to display it to the user.

override fun onNativeAdLoaded(nativeAd: NativeAdContent, ad: AdContent) {    
    // If this callback is invoked after the activity is destroyed,
    // call destroy and return to avoid potential memory leaks.
    // Note: `isDestroyed()` is a method available on Activity.
    if (isDestroyed()) {
        nativeAd.destroy()
        return
    }
    
    keepNativeAdInMemory(nativeAd)
    displayNativeAd(nativeAd)
}

Make sure to destroy all NativeAdContent references in your activity’s onDestroy() function:

override fun onDestroy() {
    for (ad in loadedNativeAds) {
        ad.destroy()
    }
    super.onDestroy()
}

Display an Ad

Once you have loaded an ad, all that remains is to display it to your users.

For the Native Ad format, there is the corresponding CASNativeView class. This class is a ViewGroup that publishers should use as the root for the NativeAdContent. A single CASNativeView corresponds to a single native ad. Each view used to display that ad's assets (the ImageView that displays the screenshot asset, for instance) should be a child of the CASNativeView object.

Display native ad Templates

The easiest way to integrate native ads into your app is via the templates API. You just need to specify the size in which you want to display the native ad and create a CASNativeView and setNativeAdTemplate with the AdSize parameter.

fun displayNativeAd(nativeAd: NativeAdContent) {
    val adSize = AdSize.MEDIUM_RECTANGLE
    val adView = CASNativeView(this)
    adView.setNativeAdTemplate(nativeAd, adSize)
    container.removeAllViews()
    container.addView(adView)

    // You have the flexibility to modify the view as needed to suit your preferences.
    // Default values are shown below.
    adView.setBackgroundColor(Color.WHITE)  
    adView.callToActionView?.setBackgroundResource(R.drawable.cas_rounded_button)
    adView.headlineView?.setTextColor(Color.parseColor("#80000000"))
}

Manual display native ad

The view hierarchy for a native ad that uses a LinearLayout to display its asset views might look like this:

<com.cleveradssolutions.sdk.nativead.CASNativeView
    xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout android:orientation="vertical">
        <LinearLayout android:orientation="horizontal">
            <ImageView android:id="@+id/ad_app_icon" />
            <TextView android:id="@+id/ad_headline" />
            <com.cleveradssolutions.sdk.nativead.CASChoicesView
                android:id="@+id/ad_choices_view" />
        </LinearLayout>
        <com.cleveradssolutions.sdk.nativead.CASMediaView
            android:id="@+id/ad_media_view" />
        <com.cleveradssolutions.sdk.nativead.CASStarRatingView
            android:id="@+id/ad_star_rating" />

        <!-- Other assets such as image or call to action, etc follow. -->
    </LinearLayout>
</com.cleveradssolutions.sdk.nativead.CASNativeView>

Here is an example that creates a CASNativeView and populates it with a NativeAdContent:

fun displayNativeAd(nativeAd: NativeAdContent) {
    val adView = inflateNativeAdView(container)
    populateNativeAdView(adView) 
    
    adView.setNativeAd(nativeAd)
    
    container.removeAllViews()
    container.addView(adView)
}

Note that all assets for a given native ad should be rendered inside the CASNativeView layout.

Here are the individual tasks:

1. Inflate the layout

Assumes that your ad layout is in a file call native_ad_layout.xml in the res/layout folder.

fun inflateNativeAdView(parent: ViewGroup): CASNativeView {
    val inflater = parent.context
        .getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
    return inflater.inflate(R.layout.native_ad_layout, parent) as NativeAdView
}

2. Populate and register the asset views

To populate the ad view with content, you need to register all the asset views that may be used.

fun populateNativeAdView(adView: CASNativeView) {
    // You can also omit adChoicesView and it will be created automatically.
    adView.adChoicesView = adView.findViewById<CASChoicesView>(R.id.ad_choices_view)  
    
    adView.mediaView = adView.findViewById<CASMediaView>(R.id.ad_media_view)  
    adView.adLabelView = adView.findViewById<TextView>(R.id.ad_label)  
    adView.headlineView = adView.findViewById<TextView>(R.id.ad_headline)  
    adView.iconView = adView.findViewById<ImageView>(R.id.ad_icon)  
    adView.callToActionView = adView.findViewById<Button>(R.id.ad_call_to_action)  
    adView.bodyView = adView.findViewById<TextView>(R.id.ad_body)  
    adView.advertiserView = adView.findViewById<TextView>(R.id.ad_advertiser)  
    adView.storeView = adView.findViewById<TextView>(R.id.ad_store)  
    adView.priceView = adView.findViewById<TextView>(R.id.ad_price)  
    adView.reviewCountView = adView.findViewById<TextView>(R.id.ad_review_count) 
    adView.starRatingView = adView.findViewById<View>(R.id.ad_star_rating)  
}

Values for registered views will be set automatically when calling setNativeAd().
Note that views for which the loaded ad does not provide content will be hidden with view.visibility = View.GONE.

3. Handle clicks

Don't implement any custom click handlers on any views over or within the native ad view. To observe click events yourself, use the ad callback onNativeAdClicked. Clicks on the ad view assets are handled by the SDK as long as you correctly populate and register the asset views, as discussed in the previous section.

4. Register the native ad object

This final step registers the native ad object with the view that's responsible for displaying it:

adView.setNativeAd(nativeAd)

Note that you can also reuse an existing CASNativeView if one is already present in your fragment or activity, or you can create an instance dynamically without using a layout file. Simply call setNativeAd for each new NativeAdContent.

Native Ad assets

Name Type Required Included Description
adChoicesView CASChoicesView Yes Always Read below about AdChoices overlay.
mediaView CASMediaView Yes Always Read below about Media Content.
headlineView TextView Yes Always The headline text of the native ad.
adLabelView TextView If provided Not always Read below about Ad attribution.
callToActionView Button If provided Not always Button that encourages users to take action (for example, "Visit site" or "Install"). This text may truncate after 15 characters.
iconView TextView If provided Always for app install ads The small app icon or advertiser logo with square aspect ratio (1:1).
bodyView TextView No Always The body text of the native ad. This text may truncate after 90 characters.
advertiserView TextView No Always for content ads Text that identifies the advertiser (for example, advertiser name, brand name, or visible URL). This text may truncate after 25 characters.
storeView TextView No Not always The name of the store where the product or service is available.
priceView TextView No Not always The price of the product or service advertised.
reviewCountView TextView No Not always The number of reviews the app has received.
starRatingView View No Not always The rating from 0-5 that represents the average rating of the app in a store.

Media Content

You're required to use the CASMediaView asset instead of the ImageView asset if you want to include a main image asset in the layout for your native ad.
The CASMediaView is a special View designed to display the main media asset, either video or image. Can be defined in an XML layout or constructed dynamically. It should be placed within the view hierarchy of a CASNativeView, just like any other asset view. The media view will be populated automatically when calling setNativeAd().

  • Check if the video asset is available using nativeAd.hasVideoContent.
  • Check if the image asset is available using nativeAd.hasImageContent.
  • Obtain the recommended aspect ratio of the media content using nativeAd.mediaContentAspectRatio.

Ad attribution

You must clearly display the text "Ad", "Advertisement", or "Sponsored" (localized appropriately). The badge is required to be a minimum of 15px height and width. Ad attribution must be displayed at the top of the ad.

AdChoices overlay

Ad AdChoices overlay logo must be displayed at the top of the ad Each ad view must display an AdChoices overlay logo. Also, it's important that the AdChoices overlay be easily seen, so choose background colors and images appropriately.

AdChoices placements

An AdChoices overlay can be added by the SDK if CASNativeView.adChoicesView not registered. Leave space in your preferred corner of your native ad view for the automatically inserted AdChoices logo.
Set CASNativeLoader.adChoicesPlacement by AdChoicesPlacement constants. By default the AdChoices icon position is set to the top right.

AdChoices custom view

The AdChoices custom view feature lets you position the AdChoices icon in a custom location. This is different from AdChoices position controls, which only allows specification of one of the four corners.
The following example demonstrates how to set a custom AdChoices view, with the AdChoices icon rendered inside the CASChoicesView.

val adChoidesView = CASChoicesView(context)
adView.adChoicesView = adChoidesView

Note

Google Ads does not support adView.adChoicesView. Instead, specify the desired AdChoicesPlacement for the logo. Other ad sources will display their logo in your custom view.

Star Rating view

The rating from 0.0-5.0 that represents the average rating of the app in a store.

  • Use CASStarRatingView template with auto populate rating value.
val starRatingView = CASStarRatingView(this)
// Optionaly change space and color
starRatingView.space = spaceInPixels
starRatingView.color = Color.parseColor("#4285F4")

adView.starRatingView = starRatingView
  • Use custom view with auto populate rating value. Implement NativeAdStarRating interface in your custom view.
adView.starRatingView = customStarRatingView
  • Use custom view with manual populate rating value.
nativeAd.starRating?.let {
    customStarRatingView.rating = it
    adView.starRatingView = customStarRatingView
}

Size and layout

Native ads smaller than 32x32dp won't serve. Ads this small can be difficult to see or interact with and may adversely affect the display quality of advertiser assets.

For native video ads, the main asset mediaView must be at least 120x120dp. Video ads won't serve to implementations with main asset CASMediaViewssmaller than 120dp in any dimension.

⚠️ **GitHub.com Fallback** ⚠️