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.
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.
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.
}
}
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.
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()
}
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.
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"))
}
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:
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
}
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
.
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.
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
.
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. |
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
.
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.
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.
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.
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.
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
}
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 CASMediaViews
smaller than 120dp in any dimension.