First Look

Give CloudX the first chance to fill an ad placement, then fall back to your existing mediation setup

First Look gives CloudX the first chance to fill a placement while preserving your existing mediation setup as the fallback path. Start with one placement, verify load and show behavior, then expand to more placements.

This example uses an AdMob interstitial fallback. The same pattern applies to other fallback mediators: load CloudX first, load the fallback only if CloudX does not load, and keep the app flow moving when neither source is ready.

FirstLookInterstitialController.kt
import android.app.Activity
import com.google.android.gms.ads.AdError
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.FullScreenContentCallback
import com.google.android.gms.ads.LoadAdError
import com.google.android.gms.ads.interstitial.InterstitialAd
import com.google.android.gms.ads.interstitial.InterstitialAdLoadCallback
import io.cloudx.sdk.CloudX
import io.cloudx.sdk.CloudXAd
import io.cloudx.sdk.CloudXError
import io.cloudx.sdk.CloudXInterstitialAd
import io.cloudx.sdk.CloudXInterstitialListener

class FirstLookInterstitialController(
    private val activity: Activity,
    private val cloudXAdUnitId: String,
    private val adMobAdUnitId: String,
) {
    enum class Source {
        CLOUDX,
        ADMOB,
    }

    interface Listener {
        fun onAdLoaded(source: Source) {}
        fun onAdLoadFailed(source: Source, message: String?) {}
        fun onAdShown(source: Source) {}
        fun onAdShowFailed(source: Source, message: String?) {}
        fun onAdClosed(source: Source) {}
        fun onAdClicked(source: Source) {}
    }

    var listener: Listener? = null

    private val cloudXInterstitial: CloudXInterstitialAd =
        CloudX.createInterstitial(activity, cloudXAdUnitId)

    private var adMobInterstitial: InterstitialAd? = null
    private var isLoadingCloudX = false
    private var isLoadingAdMob = false

    init {
        cloudXInterstitial.listener = createCloudXListener()
    }

    fun load() {
        if (isLoadingCloudX || isLoadingAdMob || cloudXInterstitial.isAdReady || adMobInterstitial != null) {
            return
        }

        isLoadingCloudX = true
        cloudXInterstitial.load()
    }

    fun show(): Boolean {
        if (cloudXInterstitial.isAdReady) {
            cloudXInterstitial.show(activity)
            return true
        }

        return showAdMobFallback()
    }

    fun destroy() {
        cloudXInterstitial.destroy()
        adMobInterstitial = null
        listener = null
    }

    private fun createCloudXListener() = object : CloudXInterstitialListener {
        override fun onAdLoaded(cloudXAd: CloudXAd) {
            isLoadingCloudX = false
            listener?.onAdLoaded(Source.CLOUDX)
        }

        override fun onAdLoadFailed(adUnitId: String, cloudXError: CloudXError) {
            isLoadingCloudX = false
            loadAdMobFallback()
        }

        override fun onAdDisplayed(cloudXAd: CloudXAd) {
            listener?.onAdShown(Source.CLOUDX)
        }

        override fun onAdDisplayFailed(cloudXAd: CloudXAd, cloudXError: CloudXError) {
            if (showAdMobFallback()) {
                return
            }

            listener?.onAdShowFailed(Source.CLOUDX, cloudXError.message)
        }

        override fun onAdHidden(cloudXAd: CloudXAd) {
            listener?.onAdClosed(Source.CLOUDX)
        }

        override fun onAdClicked(cloudXAd: CloudXAd) {
            listener?.onAdClicked(Source.CLOUDX)
        }
    }

    private fun loadAdMobFallback() {
        if (isLoadingAdMob || adMobInterstitial != null) {
            return
        }

        isLoadingAdMob = true
        InterstitialAd.load(
            activity,
            adMobAdUnitId,
            AdRequest.Builder().build(),
            object : InterstitialAdLoadCallback() {
                override fun onAdLoaded(interstitialAd: InterstitialAd) {
                    isLoadingAdMob = false
                    adMobInterstitial = interstitialAd.apply {
                        fullScreenContentCallback = createAdMobFullScreenCallback()
                    }
                    listener?.onAdLoaded(Source.ADMOB)
                }

                override fun onAdFailedToLoad(loadAdError: LoadAdError) {
                    isLoadingAdMob = false
                    adMobInterstitial = null
                    listener?.onAdLoadFailed(Source.ADMOB, loadAdError.message)
                }
            },
        )
    }

    private fun showAdMobFallback(): Boolean {
        val ad = adMobInterstitial ?: return false
        ad.show(activity)
        return true
    }

    private fun createAdMobFullScreenCallback() = object : FullScreenContentCallback() {
        override fun onAdShowedFullScreenContent() {
            adMobInterstitial = null
            listener?.onAdShown(Source.ADMOB)
        }

        override fun onAdDismissedFullScreenContent() {
            adMobInterstitial = null
            listener?.onAdClosed(Source.ADMOB)
        }

        override fun onAdFailedToShowFullScreenContent(adError: AdError) {
            adMobInterstitial = null
            listener?.onAdShowFailed(Source.ADMOB, adError.message)
        }

        override fun onAdClicked() {
            listener?.onAdClicked(Source.ADMOB)
        }
    }
}

Use show() at the placement moment. If it returns false, neither CloudX nor the fallback had a ready ad, so continue the app flow without showing an ad.

if (!firstLookInterstitial.show()) {
    continueToNextScreen()
}

After a show, close, or terminal failure, call load() again when your app is ready to prepare the next placement opportunity.