GDPR and CCPA
Keep in mind that it’s best to contact qualified legal professionals, if you haven’t done so already, to get more information and be well-prepared for compliance
The General Data Protection Regulation, better known as GDPR, took effect on May 25, 2018. It’s a set of rules designed to give EU citizens more control over their personal data. Any businesses established in the EU or with users based in Europe are required to comply with GDPR or risk facing heavy fines. The California Consumer Privacy Act (CCPA) went into effect on January 1, 2020.
We have put together some resources below to help publishers understand better the steps they need to take to be GDPR compliant.
You can learn more about GDPR and CCPA and their differences here
Step 1. Update Privacy Policy
1.1 Make Sure Your Privacy Policy Includes Information About Advertising ID Collection.
Don’t forget to add information about IP address and advertising ID collection, as well as the link to Appodeal’s privacy policy to your app’s privacy policy in Google Play.
To speed up the process, you could use privacy policy generators —just insert advertising ID, IP address, and location (if you collect a user’s location) in the "Personally Identifiable Information you collect" field (in line with other information about your app) and the link to Appodeal’s privacy policy in "Link to the privacy policy of third party service providers used by the app".
1.2 Add A Privacy Policy To Your Mobile App.
You must add your explicit privacy policies in two places: your app’s Store Listing page and within your app.
You can find detailed instructions on adding your privacy policy to your app on legal service websites. For example, Iubenda, the solution tailored to legal compliance, provides a comprehensive guide on including a privacy policy in your app.
Make sure that your privacy policy website has an SSL-certificate—this point might seem to be obvious, but it’s still essential.
Here’s are two useful resources that you can utilize while working on your app compliance:
Please note that although we’re always eager to back you up with valuable information, we’re not authorised to provide any legal advice. It’s important to address your questions to lawyers who work specifically in this area.
Step 2. Stack Consent Manager
In order for Appodeal and our ad providers to deliver ads that are more relevant to your users, as a mobile app publisher, you need to collect explicit user consent in the regions covered by GDPR and CCPA.
To get consent for collecting personal data of your users, we suggest you use a ready-made solution Stack Consent Manager.
Stack Consent Manager comes with a pre-made consent window that you can easily present to your users. That means you no longer need to create your own consent window.
Starting from Appodeal SDK 3.0, Stack Consent Manager is included by default.
Consent will be requested automatically on SDK initialization, and consent form will shown if it is necessary without any additional calls.
- Kotlin
- Java
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Appodeal.initialize(activity, appKey, adTypes, object : ApdInitializationCallback {
override fun onInitializationFinished(list: List<ApdInitializationError>?) {
//Appodeal initialization finished
}
})
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Appodeal.initialize(activity, appKey, adTypes, new ApdInitializationCallback() {
@Override public void onInitializationFinished(List<? extends ApdInitializationError> list) {
//Appodeal initialization finished
}
});
}
Advanced
Stack Consent Manager is included in Appodeal SDK by default. Consent will be requested automatically on SDK initialization , and consent form will be shown if it is necessary without any additional calls.
You can still use your own Consent Manager or customize ours by following the steps below.
Update User Consent
If you want the user to be able to change their mind about the value of Consent, then you can update it using the method to process and pass the user's consent:
- Kotlin
- Java
Appodeal.updateGDPRUserConsent(GDPRUserConsent.Personalized)
Appodeal.updateCCPAUserConsent(CCPAUserConsent.OptIn)
Appodeal.updateGDPRUserConsent(GDPRUserConsent.Personalized);
Appodeal.updateCCPAUserConsent(CCPAUserConsent.OptIn);
Use the following values to set user consent for GDPR and CCPA.
GDPRUserConsent.Unknown
- the status is unknown. The consent window wasn't shown.
GDPRUserConsent.Personalized
- the user has granted consent for personalized ads.
GDPRUserConsent.NonPersonalized
- the user has NOT granted consent for personalized ads.
CCPAUserConsent.Unknown
- the status is unknown. The consent window wasn't shown.
CCPAUserConsent.OptIn
- the user has granted consent for personalized ads.
CCPAUserConsent.OptOut
- the user has NOT granted consent for personalized ads.
You can use these methods to provide the GDPR/CCPA user consent for ad networks in Appodeal SDK anywhere in your application. You can read more info about GDPR/CCPA here.
Manage Stack Consent
If you wish, you can manage and update consent manually using Stack Consent Manager calls.
Update Consent Status
To update the consent, call the method:
- Kotlin
- Java
override fun onCreate(savedInstanceState: Bundle?) {
ConsentManager.requestConsentInfoUpdate(this, "YOUR_APP_KEY", object: ConsentInfoUpdateListener() {
override fun onConsentInfoUpdated(consent: Consent) {
super.onConsentInfoUpdated(consent)
// User's consent status successfully updated.
// Initialize the Appodeal SDK with the received Consent object here or show consent window.
}
override fun onFailedToUpdateConsentInfo(error: ConsentManagerError) {
super.onFailedToUpdateConsentInfo(error)
// User's consent status failed to update.
// Initialize the Appodeal SDK with default params.
}
})
}
@Override
protected void onCreate(Bundle savedInstanceState) {
ConsentManager.requestConsentInfoUpdate(this, "YOUR_APP_KEY", new ConsentInfoUpdateListener() {
@Override
public void onConsentInfoUpdated(@NonNull Consent consent) {
super.onConsentInfoUpdated(consent);
// User's consent status successfully updated.
// Initialize the Appodeal SDK with the received Consent object here or show consent window.
}
@Override
public void onFailedToUpdateConsentInfo(@NonNull ConsentManagerError consentManagerError) {
super.onFailedToUpdateConsentInfo(consentManagerError);
// User's consent status failed to update.
// Initialize the Appodeal SDK with default params.
}
});
}
requestConsentInfoUpdate
can be requested at any moment of the application lifecycle.
We recommend call request it at the application launch. Multiple request calls are allowed.
Required parameters:
YOUR_APP_KEY
- Appodeal app key, you can get it in your personal account;
ConsentInfoUpdateListener
- listener for result request.
If the consent information is successfully updated, the updated consent is provided via the
onConsentInfoUpdated()
method of the ConsentInfoUpdateListener
.
Now you can receive information about the previous user consent and regulation zone.
- Kotlin
- Java
// Get consent status
val consentStatus = consent.status
// Get consent status
Consent.Status consentStatus = consent.getStatus();
Before request these parameters are undefined.
Consent Status | Definition |
---|---|
UNKNOWN | The user has neither granted nor declined consent for personalized or non-personalized ads. |
NON_PERSONALIZED | The user has granted consent for non-personalized ads. |
PARTLY_PERSONALIZED | The user has granted partly(for a few Ad networks) consent for personalized ads. |
PERSONALIZED | The user has granted consent for personalized ads. |
Necessity Of Showing The Consent Window
After the onConsentInfoUpdated method was called, you need to determine if your users are subject to the GDPR and CCPA and whether you should show the consent window for the collection of personal data.
You can check whether to show a Consent Dialog or not. Before requestConsentInfoUpdate
these
parameters are undefined and there is a false
value.
- Kotlin
- Java
// Get current ShouldShow status
var consentShouldShow = ConsentManager.shouldShow
if (consentShouldShow) {
// show dialog
}
// Get current ShouldShow status
boolean consentShouldShow = ConsentManager.getShouldShow();
if (consentShouldShow) {
// show dialog
}
Show Consent Window
SDK allows calling consent window API only after request.
After the SDK requests, you can build and load the consent window. Loading allowed in any regulation zone and independent from previous consent.
The required parameter is Context
.
Optional parameter is ConsentFormListener
.
- Kotlin
- Java
// Create new Consent form listener
val consentFormListener = object : ConsentFormListener() {
override fun onConsentFormLoaded(consentForm: ConsentForm) {
// Consent form was loaded. Now you can display consent form as activity or as dialog
}
override fun onConsentFormError(error: ConsentManagerError) {
// Consent form loading or showing failed. More info can be found in 'error' object
// Initialize the Appodeal SDK with default params.
}
override fun onConsentFormOpened() {
// Consent form was shown
}
override fun onConsentFormClosed(consent: Consent) {
// Consent form was closed, you may initialize Appodeal SDK here
}
}
// Create new Consent form instance
val consentForm = ConsentForm(this, consentFormListener)
consentForm.load()
// Create new Consent form listener
IConsentFormListener consentFormListener = new ConsentFormListener() {
@Override
public void onConsentFormLoaded(@NonNull ConsentForm consentForm) {
// Consent form was loaded. Now you can display consent form as activity or as dialog
}
@Override
public void onConsentFormError(ConsentManagerError error) {
// Consent form loading or showing failed. More info can be found in 'error' object
// Initialize the Appodeal SDK with default params.
}
@Override
public void onConsentFormOpened() {
// Consent form was shown
}
@Override
public void onConsentFormClosed(Consent consent) {
// Consent form was closed, you may initialize Appodeal SDK here
}
};
// Create new Consent form instance
ConsentForm consentForm = new ConsentForm(context, consentFormListener);
consentForm.load();
You can check that the consent window is ready or not:
- Kotlin
- Java
// Indicates that consent window ready to present
val loaded = consentForm.isLoaded
// Indicates that consent window ready to present
boolean loaded = consentForm.isLoaded();
You can check that the consent window is showing or not:
- Kotlin
- Java
// Indicates that consent window is showing
val showing = consentForm.isShowing
// Indicates that consent window is showing
boolean showing = consentForm.isShowing();
After the consent window Is ready you can show it.
- Kotlin
- Java
// Show consent dialog
consentForm.show()
// Show consent dialog
consentForm.show();
After the first display of the consent window, the ConsentManager.shouldShow
will be FALSE
in
the next sessions.
Change The User Mind About The Value Of Consent.
If you want the user to be able to change their mind about the value of Consent, then you can update it using the method if you use the Stack Consent Manager API to process and pass the user's consent.
- Kotlin
- Java
Appodeal.updateConsent(consent)
Appodeal.updateConsent(consent);
Advanced
You can force consent manager to write iAB keys in SharedPreference
by setting up storage
property before the request to ConsentManager.Storage
SDK does not remove iAB keys from SharedPreference
and only overrides them.
- Kotlin
- Java
// Set storage
ConsentManager.storage = ConsentManager.Storage.SHARED_PREFERENCE
// Get storage
val iabStorage = ConsentManager.storage
// Set storage
ConsentManager.setStorage(ConsentManager.Storage.SHARED_PREFERENCE);
// Get storage
ConsentManager.Storage iabStorage = ConsentManager.getStorage();
Example
You need to update the Consent value before Appodeal SDK initialization. The code below demonstrates how to show the Stack Consent Window and update the consent value if you need to separate Consent Window Showing and Appodeal SDK initialization. After the consent value is updated, you can initialize Appodeal SDK.
- Kotlin
- Java
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestConsentUpdate()
}
fun initializeAppodeal() {
Appodeal.initialize(this, appKey, adTypes) // Use this method to prevent extra ConsentInfoUpdate request.
}
fun showConsent() {
val consentFormListener = object : ConsentFormListener() {
override fun onConsentFormLoaded(consentForm: ConsentForm) {
// Consent form was loaded. Now you can display consent form
consentForm.show()
}
override fun onConsentFormError(error: ConsentManagerError) {
// Consent form loading or showing failed. More info can be found in 'error' object
// Initialize the Appodeal SDK here.
initializeAppodeal()
}
override fun onConsentFormOpened() {
// Consent form was shown
}
override fun onConsentFormClosed(consent: Consent) {
// Consent form was closed. Update consant value here.
Appodeal.updateConsent(consent)
// Initialize the Appodeal SDK here.
initializeAppodeal()
}
}
// Create new Consent form instance
val consentForm = ConsentForm(this, consentFormListener)
// Show the consent form
consentForm.load()
}
fun requestConsentUpdate() {
// Update Consent Status. Required for show Stack Consent Window.
ConsentManager.requestConsentInfoUpdate(this, appKey, object : ConsentInfoUpdateListener() {
override fun onConsentInfoUpdated(consent: Consent) {
super.onConsentInfoUpdated(consent)
// User's consent status successfully updated.
// Show consent window to get a user consent.
if (ConsentManager.shouldShow) {
showConsent()
} else {
// Initialize the Appodeal SDK here.
initializeAppodeal()
}
}
override fun onFailedToUpdateConsentInfo(error: ConsentManagerError) {
super.onFailedToUpdateConsentInfo(error)
// User's consent status failed to update.
// Initialize the Appodeal SDK here.
initializeAppodeal()
}
})
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestConsentUpdate();
}
void initializeAppodeal() {
Appodeal.initialize(this, appKey, adTypes); // Use this method to prevent extra ConsentInfoUpdate request.
}
void showConsent() {
IConsentFormListener consentFormListener = new ConsentFormListener() {
@Override
public void onConsentFormLoaded(@NonNull ConsentForm consentForm) {
// Consent form was loaded. Now you can display consent form
consentForm.show();
}
@Override
public void onConsentFormError(ConsentManagerError error) {
// Consent form loading or showing failed. More info can be found in 'error' object
// Initialize the Appodeal SDK here.
initializeAppodeal();
}
@Override
public void onConsentFormOpened() {
// Consent form was shown
}
@Override
public void onConsentFormClosed(Consent consent) {
// Consent form was closed. Update consant value here.
Appodeal.updateConsent(consent);
// Consent value was updated.
// Initialize the Appodeal SDK here.
initializeAppodeal();
}
};
// Create new Consent form instance
ConsentForm consentForm = new ConsentForm(this, consentFormListener);
// Show the consent form
consentForm.load();
}
void requestConsentUpdate() {
ConsentManager.requestConsentInfoUpdate(this, appKey, new ConsentInfoUpdateListener() {
@Override
public void onConsentInfoUpdated(@NonNull Consent consent) {
super.onConsentInfoUpdated(consent);
// User's consent status successfully updated.
// Show consent window to get a user consent.
if (ConsentManager.getShouldShow()) {
showConsent();
} else {
// Initialize the Appodeal SDK here.
initializeAppodeal();
}
}
@Override
public void onFailedToUpdateConsentInfo(@NonNull ConsentManagerError consentManagerError) {
super.onFailedToUpdateConsentInfo(consentManagerError);
// User's consent status failed to update.
// Initialize the Appodeal SDK here.
initializeAppodeal();
}
});
}
Custom Consent Window
If you don't want to use the Stack Content Manager SDK, you can use another solution to get user's consent and provide it before Appodeal SDK initialization.
Publishers need to pass the consent before the initialize method of our SDK using the following methods.
- Kotlin
- Java
Appodeal.updateGDPRUserConsent(GDPRUserConsent.Personalized)
Appodeal.updateCCPAUserConsent(CCPAUserConsent.OptIn)
Appodeal.updateGDPRUserConsent(GDPRUserConsent.Personalized);
Appodeal.updateCCPAUserConsent(CCPAUserConsent.OptIn);
Use the following values to set user consent for GDPR and CCPA.
GDPRUserConsent.Unknown
- the status is unknown. The consent window wasn't shown.
GDPRUserConsent.Personalized
- the user has granted consent for personalized ads.
GDPRUserConsent.NonPersonalized
- the user has NOT granted consent for personalized ads.
CCPAUserConsent.Unknown
- the status is unknown. The consent window wasn't shown.
CCPAUserConsent.OptIn
- the user has granted consent for personalized ads.
CCPAUserConsent.OptOut
- the user has NOT granted consent for personalized ads.
Providing consent values before initialization prevents the build-in showing of the Stack Consent Window. You should use this approach to disable the consent window shown in GDPR/CCPA zones or if you want the user to be able to change their mind about the value of Consent.
Example
The example below shows the case of showing a Custom Content Window using the Stack Consent Manager Logic for determination of showing necessity.
- Kotlin
- Java
fun initializeAppodeal() {
Appodeal.initialize(this, appKey, adTypes) // Use this method to prevent extra ConsentInfoUpdate request.
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ConsentManager.requestConsentInfoUpdate(this, appKey, object: ConsentInfoUpdateListener() {
private fun updateCcpaConsentValue(wasGranted: Boolean) {
if (wasGranted) {
Appodeal.updateCCPAUserConsent(CCPAUserConsent.OptIn)
} else {
Appodeal.updateCCPAUserConsent(CCPAUserConsent.OptOut)
}
}
private fun updateGdprConsentValue(wasGranted: Boolean) {
if (wasGranted) {
Appodeal.updateGDPRUserConsent(GDPRUserConsent.Personalized)
} else {
Appodeal.updateGDPRUserConsent(GDPRUserConsent.NonPersonalized)
}
}
override fun onConsentInfoUpdated(consent: Consent) {
if (ConsentManager.shouldShow) {
var customConsentValue = // true or false. Show your custom consent to get the user decision.
updateCcpaConsentValue(customConsentValue)
updateGdprConsentValue(customConsentValue)
}
initializeAppodeal()
}
override fun onFailedToUpdateConsentInfo(error: ConsentManagerError) {
initializeAppodeal()
}
})
}
void initializeAppodeal() {
Appodeal.initialize(this, appKey, adTypes); // Use this method to prevent extra ConsentInfoUpdate request.
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ConsentManager.requestConsentInfoUpdate(this, appKey, new ConsentInfoUpdateListener() {
private void updateCcpaConsentValue(boolean wasGranted) {
if (wasGranted) {
Appodeal.updateCCPAUserConsent(CCPAUserConsent.OptIn);
} else {
Appodeal.updateCCPAUserConsent(CCPAUserConsent.OptOut);
}
}
private void updateGdprConsentValue(boolean wasGranted) {
if (wasGranted) {
Appodeal.updateGDPRUserConsent(GDPRUserConsent.Personalized);
} else {
Appodeal.updateGDPRUserConsent(GDPRUserConsent.NonPersonalized);
}
}
@Override
public void onConsentInfoUpdated(@NonNull Consent consent) {
if (ConsentManager.getShouldShow()) {
boolean customConsentValue = // true; or false; Show your custom consent to get the user decision.
updateCcpaConsentValue(customConsentValue);
updateGdprConsentValue(customConsentValue);
}
initializeAppodeal();
}
@Override
public void onFailedToUpdateConsentInfo(@NonNull ConsentManagerError consentManagerError) {
initializeAppodeal();
}
});
}