Home / Programming / Android / Android Admob Consent SDK : All you need to know + Example

Android Admob Consent SDK : All you need to know + Example

First of all, let me address Google’s complacency to help app developers implement the GDPR SDK. They haven’t released a video on youtube neither have they released a tutorial on CodeLabs nor localise the content of Consent Dialog – all written in English (as of this writing).

What is GDPR?

GDPR is the new European privacy regulation that took effect on 25th of May 2018. The new set of rules is causing many companies to lose sleep also due to the recent Disney lawsuit and the lawsuit against Kiloo, maker of Subway Surfer. The first suit includes not just the company itself but also tech providers such as Kochava, Upsight and Unity.

Why should you Implement Admob Consent SDK?

Although Google has yet to explicitly dictate the penalties for non GDPR compliant Apps.

I wish I could tell you that this stuff won’t be enforced but no one knows how strictly it will be enforced and when.

The ‘interpretation of the law’ is still a bit vague — because the wording is full of impenetrable jargon / generalised advice.   For example, GDPR is outlined in an 88-page document about digital data privacy, that only mentions the word cookie once.

Here are some likely scenarios:

  1. Depending on how popular your app is, you could pay a €20M fine or 4% of annual gross revenues as Fine.
  2. You risk getting your app delisted or banned from the Google Play Store
  3. You risk losing your adMob account. When that happens, only a miracle can let Google reopen it, because Google Customer Care is very hard to reach

when should you implement GDPR Consent Policy?

As long as you collect  and use any user data whatsoever within your app, even as simple as anonymous Analytics data, you need to implement the GDPR policy.

Read more about  Google User Consent Policy 

How to Implement Admob Consent SDK

Grab Source Code on GitHub

Step 1: Import Consent SDK

i. Within build.gradle(project)

allprojects {
     repositories {
         jcenter()
         maven {
             url "https://maven.google.com"
         }
     }
}

ii. Within build.gradle(app)

dependencies {
     implementation 'com.google.android.ads.consent:consent-library:1.0.3'
}

Step 2 : Change Admob Settings

Open adMob dashboard > Blocking Controls > EU User Consent.

Within Select Ad Technology Providers select Custom set of Ad Technology Providers. if you are using Ad Mediation click select providers and choose maximum of 12 ad networks you use.

Step 3 : Activity Layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 xmlns:app="http://schemas.android.com/apk/res-auto"
 xmlns:tools="http://schemas.android.com/tools"
 android:layout_width="match_parent"
 android:layout_height="match_parent"
 xmlns:ads="http://schemas.android.com/apk/res-auto"
 tools:context=".MainActivity">

<com.google.android.gms.ads.AdView
 android:id="@+id/adViewMain"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginBottom="8dp"
 android:layout_marginEnd="8dp"
 android:layout_marginStart="8dp"
 ads:adSize="SMART_BANNER"
 ads:adUnitId="ca-app-pub-3940256099942544/6300978111"
 app:layout_constraintBottom_toBottomOf="parent"
 app:layout_constraintEnd_toEndOf="parent"
 app:layout_constraintStart_toStartOf="parent" />

<Button
 android:id="@+id/button"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginEnd="8dp"
 android:layout_marginStart="8dp"
 android:layout_marginTop="32dp"
 android:onClick="showInterstitialAd"
 android:text="Click To Show Interstitial Ad"
 app:layout_constraintEnd_toEndOf="@+id/button2"
 app:layout_constraintStart_toStartOf="@+id/button2"
 app:layout_constraintTop_toTopOf="parent" />

<Button
 android:id="@+id/button2"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 android:layout_marginEnd="8dp"
 android:layout_marginStart="8dp"
 android:layout_marginTop="8dp"
 android:onClick="showRewardedVideoAd"
 android:text="Click to Show Rewarded Video Ad"
 app:layout_constraintEnd_toEndOf="parent"
 app:layout_constraintStart_toStartOf="parent"
 app:layout_constraintTop_toBottomOf="@+id/button" />

</android.support.constraint.ConstraintLayout>

Step 4: Generate a Privacy Policy

1. Goto App Privacy Policy Generator, input details of your app and generate a Privacy and Policy document

2. If you don’t have an App Page, goto blogger.com, create a new web page then create a new page, title it Privacy and Policy – [App Name] copy the content of step 1 into this new page. Hit “Publish”

3. Copy the link of Privacy and Policy page

Step 5: MainActivity.kt

Initialise Ads

private fun initialiseAds() {
        mInterstitialAd = InterstitialAd(this)
        mInterstitialAd?.adUnitId = "ca-app-pub-3940256099942544/1033173712"
        mInterstitialAd?.adListener = object : AdListener() {
            override fun onAdClosed() {
                requestNewInterstitial()
            }
        }
        mRewardedVideoAd = MobileAds.getRewardedVideoAdInstance(this)
    }

 

Check Ad Consent

private fun checkAdConsent() {
        val consentInformation = ConsentInformation.getInstance(this)
        val publisherIds = Array(1, { "your_publisher_id" })
        consentInformation.requestConsentInfoUpdate(publisherIds, object : ConsentInfoUpdateListener {
            override fun onConsentInfoUpdated(consentStatus: ConsentStatus) {
                when (consentStatus) {
                    ConsentStatus.PERSONALIZED -> loadAds(true)
                    ConsentStatus.NON_PERSONALIZED -> loadAds(false)
                    ConsentStatus.UNKNOWN -> displayConsentForm()
                }
                Log.d(TAG, "onConsentInfoUpdated, Consent Status = ${consentStatus.name}")
            }

            override fun onFailedToUpdateConsentInfo(errorDescription: String) {
                Log.d(TAG, "onFailedToUpdateConsentInfo - $errorDescription")
            }
        })
    }

 

Load Ads

private fun loadAds(showPersonlAds: Boolean) {
        mShowPersonlAds = showPersonlAds
        val build: AdRequest
        if (mShowPersonlAds)
            build = AdRequest.Builder()
                    .addNetworkExtrasBundle(AdMobAdapter::class.java, getNonPersonalizedAdsBundle())
                    .build()
        else
            build = AdRequest.Builder().build()

        mAdView!!.adListener = object : AdListener() {
            override fun onAdClosed() {
                mAdView!!.loadAd(build)
                Log.d(TAG, "onAdClosed Banner Ad")
            }

            override fun onAdFailedToLoad(p0: Int) {
                super.onAdFailedToLoad(p0)
                mAdView!!.loadAd(build)
                Log.d(TAG, "onAdFailedToLoad Banner Ad")
            }
        }
        mAdView!!.loadAd(build)
        requestNewInterstitial()
        loadRewardedVideoAd()
    }

 

Display Consent Form

private fun displayConsentForm() {
        consentForm = ConsentForm.Builder(this, URL("https://your.privacy.url/"))
                .withListener(object : ConsentFormListener() {
                    override fun onConsentFormOpened() {
                        super.onConsentFormOpened()
                        Log.d(TAG, "Requesting Consent: onConsentFormOpened")
                    }

                    override fun onConsentFormLoaded() {
                        super.onConsentFormLoaded()
                        Log.d(TAG, "Requesting Consent: onConsentFormLoaded")
                        consentForm?.show()
                    }

                    override fun onConsentFormError(reason: String?) {
                        super.onConsentFormError(reason)
                        ConsentInformation.getInstance(this@MainActivity).consentStatus = ConsentStatus.PERSONALIZED
                        Log.d(TAG, "Requesting Consent: onConsentFormError. $reason")
                    }

                    override fun onConsentFormClosed(consentStatus: ConsentStatus?, userPrefersAdFree: Boolean?) {
                        super.onConsentFormClosed(consentStatus, userPrefersAdFree)
                        Log.d(TAG, "Requesting Consent: onConsentFormClosed")
                        var userWantsAdFree = false
                        if (userPrefersAdFree != null) {
                            userWantsAdFree = userPrefersAdFree
                        }
                        if (userWantsAdFree) {
                            // Buy or Subscribe
                            Log.d(TAG, "Requesting Consent: User prefers AdFree")
                            // TODO This is where you write your Intent to launch the purchase flow dialog

                        } else {
                            Log.d(TAG, "Requesting Consent: onConsentFormClosed. Consent Status = $consentStatus")
                            when (consentStatus) {
                                ConsentStatus.PERSONALIZED -> loadAds(true)
                                ConsentStatus.NON_PERSONALIZED -> loadAds(false)
                                ConsentStatus.UNKNOWN -> loadAds(true)
                            }
                        }
                    }
                })
                .withPersonalizedAdsOption()
                .withNonPersonalizedAdsOption()
      //          .withAdFreeOption() TODO enable this option if you have created an inApp Purchase to eliminate Ads
                .build()
        consentForm!!.load()
    }

All together, MainActivity.kt

class MainActivity : AppCompatActivity() {
    private var mShowPersonlAds = true
    private var mAdView: AdView? = null
    private var mInterstitialAd: InterstitialAd? = null
    private var mRewardedVideoAd: RewardedVideoAd? = null
    private var consentForm: ConsentForm? = null
    private val SHOW_PERSONAL_ADS_KEY = "show.personal.ads.key"

    val TAG = "MainActivity"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        if (savedInstanceState != null)
            mShowPersonlAds = savedInstanceState.getBoolean(SHOW_PERSONAL_ADS_KEY)
        mAdView = findViewById(R.id.adViewMain)
        initialiseAds()
        checkAdConsent()
    }

    fun showInterstitialAd(view: View) {
        if (mInterstitialAd!!.isLoaded)
            mInterstitialAd?.show()
        else
            requestNewInterstitial()
    }

    fun showRewardedVideoAd(view: View) {
        if (mRewardedVideoAd!!.isLoaded)
            mRewardedVideoAd?.show()
        else
            loadRewardedVideoAd()
    }

    private fun checkAdConsent() {
        val consentInformation = ConsentInformation.getInstance(this)
        val publisherIds = Array(1, { "pub-9297690518647609" })
        consentInformation.requestConsentInfoUpdate(publisherIds, object : ConsentInfoUpdateListener {
            override fun onConsentInfoUpdated(consentStatus: ConsentStatus) {
                when (consentStatus) {
                    ConsentStatus.PERSONALIZED -> loadAds(true)
                    ConsentStatus.NON_PERSONALIZED -> loadAds(false)
                    ConsentStatus.UNKNOWN -> displayConsentForm()
                }
                Log.d(TAG, "onConsentInfoUpdated, Consent Status = ${consentStatus.name}")
            }

            override fun onFailedToUpdateConsentInfo(errorDescription: String) {
                Log.d(TAG, "onFailedToUpdateConsentInfo - $errorDescription")
            }
        })
    }

    private fun loadAds(showPersonlAds: Boolean) {
        mShowPersonlAds = showPersonlAds
        val build: AdRequest
        if (mShowPersonlAds)
            build = AdRequest.Builder()
                    .addNetworkExtrasBundle(AdMobAdapter::class.java, getNonPersonalizedAdsBundle())
                    .build()
        else
            build = AdRequest.Builder().build()

        mAdView!!.adListener = object : AdListener() {
            override fun onAdClosed() {
                mAdView!!.loadAd(build)
                Log.d(TAG, "onAdClosed Banner Ad")
            }

            override fun onAdFailedToLoad(p0: Int) {
                super.onAdFailedToLoad(p0)
                mAdView!!.loadAd(build)
                Log.d(TAG, "onAdFailedToLoad Banner Ad")
            }
        }
        mAdView!!.loadAd(build)
        requestNewInterstitial()
        loadRewardedVideoAd()
    }

    private fun initialiseAds() {
        mInterstitialAd = InterstitialAd(this)
        mInterstitialAd?.adUnitId = "ca-app-pub-3940256099942544/1033173712"
        mInterstitialAd?.adListener = object : AdListener() {
            override fun onAdClosed() {
                requestNewInterstitial()
            }
        }
        mRewardedVideoAd = MobileAds.getRewardedVideoAdInstance(this)
    }

    private fun getNonPersonalizedAdsBundle(): Bundle {
        val extra = Bundle()
        extra.putString("npa", "1")
        return extra
    }

    private fun requestNewInterstitial() {
        val adRequest: AdRequest
        if (mShowPersonlAds) {
            adRequest = AdRequest.Builder().build()
        } else {
            adRequest = AdRequest.Builder()
                    .addNetworkExtrasBundle(AdMobAdapter::class.java, getNonPersonalizedAdsBundle())
                    .build()
        }
        mInterstitialAd!!.loadAd(adRequest)
    }

    private fun loadRewardedVideoAd() {
        val adRequest: AdRequest
        if (mShowPersonlAds) {
            adRequest = AdRequest.Builder().build()
        } else {
            adRequest = AdRequest.Builder()
                    .addNetworkExtrasBundle(AdMobAdapter::class.java, getNonPersonalizedAdsBundle())
                    .build()

        }
        mRewardedVideoAd?.loadAd("ca-app-pub-3940256099942544/5224354917", adRequest)
    }

    private fun displayConsentForm() {
        consentForm = ConsentForm.Builder(this, URL("https://your.privacy.url/"))
                .withListener(object : ConsentFormListener() {
                    override fun onConsentFormOpened() {
                        super.onConsentFormOpened()
                        Log.d(TAG, "Requesting Consent: onConsentFormOpened")
                    }

                    override fun onConsentFormLoaded() {
                        super.onConsentFormLoaded()
                        Log.d(TAG, "Requesting Consent: onConsentFormLoaded")
                        consentForm?.show()
                    }

                    override fun onConsentFormError(reason: String?) {
                        super.onConsentFormError(reason)
                        ConsentInformation.getInstance(this@MainActivity).consentStatus = ConsentStatus.PERSONALIZED
                        Log.d(TAG, "Requesting Consent: onConsentFormError. $reason")
                    }

                    override fun onConsentFormClosed(consentStatus: ConsentStatus?, userPrefersAdFree: Boolean?) {
                        super.onConsentFormClosed(consentStatus, userPrefersAdFree)
                        Log.d(TAG, "Requesting Consent: onConsentFormClosed")
                        var userWantsAdFree = false
                        if (userPrefersAdFree != null) {
                            userWantsAdFree = userPrefersAdFree
                        }
                        if (userWantsAdFree) {
                            // Buy or Subscribe
                            Log.d(TAG, "Requesting Consent: User prefers AdFree")
                            // TODO This is where you write your Intent to launch the purchase flow dialog

                        } else {
                            Log.d(TAG, "Requesting Consent: onConsentFormClosed. Consent Status = $consentStatus")
                            when (consentStatus) {
                                ConsentStatus.PERSONALIZED -> loadAds(true)
                                ConsentStatus.NON_PERSONALIZED -> loadAds(false)
                                ConsentStatus.UNKNOWN -> loadAds(true)
                            }
                        }
                    }
                })
                .withPersonalizedAdsOption()
                .withNonPersonalizedAdsOption()
      //          .withAdFreeOption() TODO enable this option if you have created an inApp Purchase to eliminate Ads
                .build()
        consentForm!!.load()
    }

    override fun onSaveInstanceState(outState: Bundle?, outPersistentState: PersistableBundle?) {
        super.onSaveInstanceState(outState, outPersistentState)
        outState?.putBoolean(SHOW_PERSONAL_ADS_KEY, mShowPersonlAds)
    }
}

Step 6: Testing

The code in MainActivity. checks the consent dialog each time the user launches the app. If the user is within the EU and has not stated before hand whether she wants to be shown personal ads or not, we’ll launch the ad consent dialog. If the user dismisses the dialog it means no preference has been selected. The dialog will continue to popup on app launch until the user decides.

Consent Dialog will only be shown if your Device is detected to be within the boundary of European Union.

Presuming your using a real physical device to debug, install a VPN app change your IP to a European country and watch the dialog popup.

Step 6: Reset Consent

Part of the consent requirement is to give the user the freedom to reset her consent.

val consentInformation = ConsentInformation.getInstance(context);
        consentInformation.reset()

Congratulations! 🤸‍♀️🎊Your app is now GDPR compliant.

For more information you can refer Admob Official page to integrate admob consent SDK.

If you enjoyed this article, show your appreciation by dropping a nice comment, follow Edge Dev Studio on FB or Star this Project on Github

Summary
Article Name
Android Admob Consent SDK + Example
Description
First of all, let me address Google’s complacency to help app developers implement the GDPR SDK. They haven’t released a video on youtube neither have they released a tutorial on CodeLabs nor localise the content of Consent Dialog - all written in English (as of this writing).
Author
Publisher Name
Edge Dev Studio
Publisher Logo

About Edge Developer

Hey there! am Opeyemi Olorunleke (aka Edge Developer), an Android developer. I Love Sharing Android Tutorials and code snippets.

Check Also

Android Intents : All you need to know + Example

What are Android Intents? Android Intent is a simple message object which is used to communicate …

Leave a Reply

Your email address will not be published. Required fields are marked *