In this post I will show you a sample of how to integrate Google User Messaging Platform and Admob into my Android applications.
Google User Messaging Platform ( UMP )
UMP is a privacy and messaging tool that is needed to be able to show ads in your apps. There are locations that require the user to be able to opt in / out of privacy options inside the app providing ads. This tools provides a way to implement a messaging system to show to the user that will allow them to choice such options. For more detailed information visit the link below.
https://developers.google.com/admob/android/privacy
In my code samples below I put together a few classes that will help to integrate Google User Messaging Platform and Admob easier for future use. The classes were pieced together from following Google's sample app describing the necessary steps. The steps are provided in this link below.
https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/privacy
Disclaimer: I am not a lawyer or a professional in data privacy. This post follows the tools and documentation provided by the SDK providers and is implemented based on their descriptions. Before proceeding, you should learn more about these topics and gather your own knowledge on the subject and /or consult with a professional.
Code
AndroidManifest.xml
First you will need to add a meta tag in your AndroidManifest.xml. The meta tag will need your Admob app ID that you can get from your console inside your App then App Settings.
<manifest>
<application>
...
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-XXXXXXXXXXXXXXXXXXXXXXXXXXX"/>
</application>
</manifest>
Libraries
build.gradle
Add the necessary libraries. First add this to your app level build.gradle.
//google ads and ump message
implementation(libs.play.services.ads)
implementation(libs.user.messaging.platform)
libs.versions.toml
Then add this to your libs.versions.toml.
[versions]
...
playServicesAds = "24.1.0"
userMessagingPlatform = "3.1.0"
[libraries]
...
play-services-ads = { module = "com.google.android.gms:play-services-ads", version.ref = "playServicesAds" }
user-messaging-platform = { module = "com.google.android.ump:user-messaging-platform", version.ref = "userMessagingPlatform" }
Classes
Next I created two helper classes to initiate the check for user consent as well as a helper class to implement functionality.
GoogleMobileAdsConsentManager
GoogleMobileAdsConsentManager class is what is used in the documentation sample app from the link above.
/*
* Copyright 2023 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import android.app.Activity
import android.content.Context
import com.google.android.ump.ConsentDebugSettings
import com.google.android.ump.ConsentForm.OnConsentFormDismissedListener
import com.google.android.ump.ConsentInformation
import com.google.android.ump.ConsentRequestParameters
import com.google.android.ump.FormError
import com.google.android.ump.UserMessagingPlatform
/**
* The Google Mobile Ads SDK provides the User Messaging Platform (Google's IAB Certified consent
* management platform) as one solution to capture consent for users in GDPR impacted countries.
* This is an example and you can choose another consent management platform to capture consent.
*/
class GoogleMobileAdsConsentManager private constructor(context: Context) {
private val consentInformation: ConsentInformation =
UserMessagingPlatform.getConsentInformation(context)
// Check your logcat output for the test device hashed ID e.g.
// "Use RequestConfiguration.Builder().setTestDeviceIds(Arrays.asList("ABCDEF012345"))
// to get test ads on this device" or
// "Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("ABCDEF012345") to set this as
// a debug device".
val TEST_DEVICE_HASHED_ID = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
/** Interface definition for a callback to be invoked when consent gathering is complete. */
fun interface OnConsentGatheringCompleteListener {
fun consentGatheringComplete(error: FormError?)
}
/** Helper variable to determine if the app can request ads. */
val canRequestAds: Boolean
get() = consentInformation.canRequestAds()
// [START is_privacy_options_required]
/** Helper variable to determine if the privacy options form is required. */
val isPrivacyOptionsRequired: Boolean
get() =
consentInformation.privacyOptionsRequirementStatus ==
ConsentInformation.PrivacyOptionsRequirementStatus.REQUIRED
// [END is_privacy_options_required]
/**
* Helper method to call the UMP SDK methods to request consent information and load/show a
* consent form if necessary.
*/
fun gatherConsent(
activity: Activity,
onConsentGatheringCompleteListener: OnConsentGatheringCompleteListener,
) {
// For testing purposes, you can force a DebugGeography of EEA or NOT_EEA.
val debugSettings =
ConsentDebugSettings.Builder(activity)
.setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA)
.addTestDeviceHashedId(TEST_DEVICE_HASHED_ID)
.build()
val params = ConsentRequestParameters.Builder()
//.setConsentDebugSettings(debugSettings)
.build()
//consentInformation.reset()
// [START request_consent_info_update]
// Requesting an update to consent information should be called on every app launch.
consentInformation.requestConsentInfoUpdate(
activity,
params,
{
// Called when consent information is successfully updated.
// [START_EXCLUDE silent]
loadAndShowConsentFormIfRequired(activity, onConsentGatheringCompleteListener)
// [END_EXCLUDE]
},
{ requestConsentError ->
// Called when there's an error updating consent information.
// [START_EXCLUDE silent]
onConsentGatheringCompleteListener.consentGatheringComplete(requestConsentError)
// [END_EXCLUDE]
},
)
// [END request_consent_info_update]
}
private fun loadAndShowConsentFormIfRequired(
activity: Activity,
onConsentGatheringCompleteListener: OnConsentGatheringCompleteListener,
) {
// [START load_and_show_consent_form]
UserMessagingPlatform.loadAndShowConsentFormIfRequired(activity) { formError ->
// Consent gathering process is complete.
// [START_EXCLUDE silent]
onConsentGatheringCompleteListener.consentGatheringComplete(formError)
// [END_EXCLUDE]
}
// [END load_and_show_consent_form]
}
/** Helper method to call the UMP SDK method to show the privacy options form. */
fun showPrivacyOptionsForm(
activity: Activity,
onConsentFormDismissedListener: OnConsentFormDismissedListener,
) {
// [START present_privacy_options_form]
UserMessagingPlatform.showPrivacyOptionsForm(activity, onConsentFormDismissedListener)
// [END present_privacy_options_form]
}
companion object {
@Volatile private var instance: GoogleMobileAdsConsentManager? = null
fun getInstance(context: Context) =
instance
?: synchronized(this) {
instance ?: GoogleMobileAdsConsentManager(context).also { instance = it }
}
}
}
Inside the gatherConsent function there is a variable named debugSettings. This is where you can implement testing with your app.
Uncomment the following two lines for:
- setConsentDebugSettings(debugSettings) - this will attach the debugSettings to the current parameters in the consent builder.
- consentInformation.reset() - this will reset the current user consent. The user will need to be asked for consent again.
val debugSettings =
ConsentDebugSettings.Builder(activity)
.setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA)
.addTestDeviceHashedId(TEST_DEVICE_HASHED_ID)
.build()
val params = ConsentRequestParameters.Builder()
//.setConsentDebugSettings(debugSettings)
.build()
//consentInformation.reset()
MyMobileAdsInitHelper
MyMobileAdsInitHelper is used to piece the backend together with the UI calls. This class contains:
- initialize the consent manager
- initialize Admob ads
- check if the app is able to display ads. The value is contained in a StateFlow to access later.
- check if the requires the consent manager to display the UMP message to the user. The value is contained in a StateFlow to access later.
class MyMobileAdsInit {
private lateinit var googleMobileAdsConsentManager: GoogleMobileAdsConsentManager
private val _mobileAdsState: MutableStateFlow<Boolean> = MutableStateFlow<Boolean>(false)
val mobileAdsState: StateFlow<Boolean> = _mobileAdsState.asStateFlow()
private val _mobileAdsPrivacyOptionReq: MutableStateFlow<Boolean> =
MutableStateFlow<Boolean>(false)
val mobileAdsPrivacyOptionReq: StateFlow<Boolean> = _mobileAdsPrivacyOptionReq.asStateFlow()
fun initMobileAds(context: Activity) {
// Log the Mobile Ads SDK version.
Log.d("Consent", "Google Mobile Ads SDK Version: " + MobileAds.getVersion())
googleMobileAdsConsentManager = GoogleMobileAdsConsentManager.getInstance(context)
googleMobileAdsConsentManager.gatherConsent(context) { error ->
if (error != null) {
// Consent not obtained in current session.
Log.d("Consent", "${error.errorCode}: ${error.message}")
_mobileAdsState.update { false }
}
if (googleMobileAdsConsentManager.canRequestAds) {
//initializeMobileAds(context)
_mobileAdsState.update { true }
} else {
_mobileAdsState.update { false }
}
if (googleMobileAdsConsentManager.isPrivacyOptionsRequired) {
// Regenerate the options menu to include a privacy setting.
//invalidateOptionsMenu()
_mobileAdsPrivacyOptionReq.update { true }
} else {
_mobileAdsPrivacyOptionReq.update { false }
}
}
// This sample attempts to load ads using consent obtained in the previous session.
if (googleMobileAdsConsentManager.canRequestAds) {
//initializeMobileAds(context)
_mobileAdsState.update { true }
} else {
_mobileAdsState.update { false }
}
}
fun showPrivacyOptionForm(context: Activity) {
googleMobileAdsConsentManager.showPrivacyOptionsForm(context) {
}
}
}
@Composable
fun initializeMobileAds(context: Activity) {
LaunchedEffect(true) {
CoroutineScope(Dispatchers.IO).launch {
MobileAds.initialize(context)
}
}
}
Activity
In your Activity, there are a few pieces of code to tie it all together.
- create an instance of the MyMobileAdsInit. This will NEED to be initialized in the DisposableEffect in the onCreate Lifecycle. Anywhere else, the app could crash or recompose until crash.
- collect the mobileAdsState StateFlow. This is a boolean that tells you if you can requests ads based on the current consent.
- collect the mobileAdsPrivacyOptionReq StateFlow. This is a boolean that tells you if you need to have a way to allow user to change their privacy settings.
The UI
class MainActivity : ComponentActivity() {
private var myMobileAdsInit : MyMobileAdsInit = MyMobileAdsInit()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
setContent {
YourAppTheme {
...
val canRequestAds = myMobileAdsInit.mobileAdsState.collectAsStateWithLifecycle().value
val mobileAdsPrivacyOptionReq = myMobileAdsInit.mobileAdsPrivacyOptionReq.collectAsStateWithLifecycle().value
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_CREATE) {
myMobileAdsInit.initMobileAds(this@MainActivity)
}
}
lifecycleOwner.lifecycle.addObserver(observer)
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
...
//check if you can request ads, if so then add composable
if(canRequestAds){
addBannerAds()
}
...
}
}
}
...
@Composable
private fun addBannerAds() {
initializeMobileAds(this@MainActivity)
BannerAd(
//ad unit from Admob console
adUnitId = "ca-app-pub-XXXXXXXXXXXXXXXXX/XXXXXXXXX"
)
}
}
Finally I will show you a basic composable of a banner ad.
BannerAd
@Composable
fun BannerAd(adUnitId: String) {
Row(modifier = Modifier.fillMaxWidth()) {
AndroidView(
factory = { context ->
AdView(context).apply {
this.setAdSize(AdSize.BANNER)
this.adUnitId = adUnitId
loadAd(AdRequest.Builder().build())
}
}, modifier = Modifier.fillMaxWidth()
)
}
}
If the app needs you to have a way for the user to change their consent in the app, you can use the mobileAdsPrivacyOptionReq StateFlow boolean.
Below is a sample of a button implementing this feature.
if(isPrivacyOptionReq) {
OutlinedButton(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp),
onClick = {
myMobileAdsInit.showPrivacyOptionForm(this@MainActivity)
}
) {
Row(
horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Filled.Settings,
contentDescription = "Open Ad Privacy Options",
modifier = Modifier.padding(10.dp)
)
Text(text = "Open Ad Privacy Options")
}
}
}
Sum Up
I hope this helps put you on the right path. There is a lot of documentation about this subject. I would highly recommend taking your time to read through them.