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)
        registerNativeAdContent(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.

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)
        registerNativeAdContent(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 register the ad here;
    // instead, call registerLastNativeAdContent() to display it later
}
...

fun registerLastNativeAdContent() {
    val nativeAd = loadedNativeAds.lastOrNull() ?: return
    if (!nativeAd.isExpired) {
        registerNativeAdContent(nativeAd)
    } else {
        loadedNativeAds.remove(nativeAd)
        registerLastNativeAdContent()
    }
}

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)
    registerNativeAdContent(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.

lateinit var adView: CASNativeView

override fun onCreate(savedInstanceState: Bundle?) {  
    super.onCreate(savedInstanceState)  
    setContentView(R.layout.activity_main)

    val container = findViewById<ViewGroup>(R.id.native_ad_container)
    inflateNativeAdView(container)
    populateNativeAdView(adView)
}

fun registerNativeAdContent(nativeAd: NativeAdContent) {
    adView.setNativeAd(nativeAd)
}

Here are the individual tasks:

  1. Inflate the layout: See below for the implementation of inflateNativeAdView(container) function.
  2. Register the asset views: 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. See below for the implementation of registerAdAssetViews(adView) function.
  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 content:
    This final step registers the native ad object with the view that's responsible for displaying it. 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.

Display native ad Templates

The easiest way to integrate native ads into your app is via the templates API. You just need to create the CASNativeView and specify the size to setAdTemplateSize.

fun inflateNativeAdView(container: ViewGroup) {
    adView = CASNativeView(this)
    val size = AdSize.MEDIUM_RECTANGLE
    adView.setAdTemplateSize(size)

    customizeAdViewAppearance(adView)
    
    container.addView(adView)
}

Note

Creating the layout may cause UI rendering to freeze briefly, so it is recommended to set the template size only once when initializing the view.

When you set the ad template size using setAdTemplateSize, the CASNativeView automatically registers all native asset views with the corresponding template assets for the selected size.

fun registerAdAssetViews(adView: CASNativeView) {
    // No manual registration is required.
}

Optional: Customize the appearance of asset views after calling setAdTemplateSize by modifying properties from the CASNativeView.

fun customizeAdViewAppearance(adView: CASNativeView) {
    // 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>

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

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

fun inflateNativeAdView(container: ViewGroup) {
    val attachToContainer = true
    val inflater = container.context.getSystemService(
        Context.LAYOUT_INFLATER_SERVICE
    ) as LayoutInflater
    
    adView = inflater.inflate(
        R.layout.native_ad_layout, container, attachToContainer
    ) as NativeAdView
}

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

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)  
}

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
}

Advertising requirements

  • 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.
  • The Ad View must be visible and non-transparent.
  • At a single point in time, a loaded ad can only be served in one View. Simultaneous display of a single ad in multiple Views may result in the loss of impression.
  • The app must be active (not running in the background).
  • Don't edit the text content of assets.
  • Don't edit the content of images.
⚠️ **GitHub.com Fallback** ⚠️