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 and App Store.

To speed up the process, you could use privacy policy generators —just insert advertising ID, IP address, and location (if you collect a user’ 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:

    Privacy, Security and Deception regulations (by Google Play)
    Recommendations on Developing a Meaningful Privacy Policy (by Attorney General California Department of Justice)

Please note that although we’re always eager to back you up with valuable information, we’re not authorized 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.

Minimal requirements: Appodeal SDK 2.7.0 or higher.

2.1. Update Consent Status 

When using the Consent SDK, it is recommended that you determine the status of a user's consent at every app launch.

Call requestConsentInfoUpdate() on an instance of ConsentManager:

ConsentManager consentManager = ConsentManager.getInstance();
consentManager.requestConsentInfoUpdate(appKey, this);

Extend your class with IConsentInfoUpdateListener:

YourClassName : IConsentInfoUpdateListener;

Now you can use use the following callback methods within your public class: 

public void onConsentInfoUpdated(Consent consent)
{
	Debug.Log("onConsentInfoUpdated");
 	// User's consent status successfully updated.
    // Initialize the Appodeal SDK with the received Consent object here or show consent window
}

public void onFailedToUpdateConsentInfo(ConsentManagerException error)
{
	Debug.Log($"onFailedToUpdateConsentInfo Reason: {error.getReason()}");
    // User's consent status failed to update.
}

All returned errors are Exception instances with custom codes:

CodeDescription
INTERNAL(1)Error on the SDK side. Includes JS-bridge or encoding/decoding errors
NETWORKING(2)HTTP errors, parse request/response 
INCONSISTENT(3)Incorrect SDK API usage


Consent manager SDK can be synchronized at any moment of the application lifecycle. We recommend synchronizing it at the application launch. Multiple synchronization calls are allowed.

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. Before request these parameters are undefined.

// Get consent status 
Consent.Status consentStatus = consentManager.getConsentStatus();

Consent Status

Definition

UNKNOWNThe user has neither granted nor declined consent for personalized or non-personalized ads.
NON_PERSONALIZEDThe user has granted consent for non-personalized ads.
PARTLY_PERSONALIZEDThe user has granted partly(for a few Ad networks) consent for personalized ads.
PERSONALIZEDThe user has granted consent for personalized ads.

2.2. 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 request these parameters are undefined(Unknown status)

// Get current ShouldShow status
Consent.ShouldShow consentShouldShow = consentManager.shouldShowConsentDialog();

if (consentShouldShow == Consent.ShouldShow.TRUE){
    // show dialog
}
ShouldShowDefinition
TRUEThe user is within the scope of the GDPR or CCPA laws, the consent window should be displayed.
FALSEThe user is not within the scope of the GDPR or CCPA laws OR the consent window has already been shown.
UNKNOWNThe value is undefined(the requestConsentInfoUpdate method was not called).

2.3. 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.

ConsentForm consentForm = new ConsentForm.Builder().withListener(this).build();

You can check that the consent window is ready or not

boolean loaded = consentForm.isLoaded();

After the consent window Is ready you can show it as Activity or Dialog

// Show consent dialog as Activity
consentForm.showAsActivity();

// Show consent dialog as Dialog
consentForm.showAsDialog();


After the first display of the consent window, the shouldShowConsentDialog method will return the value of Consent.ShouldShow.FALSE in the next sessions

Handling presentation callbacks
Extend your class with IConsentFormListener:

YourClassName : IConsentFormListener;
public void onConsentFormLoaded() { print("ConsentFormListener - onConsentFormLoaded");}

public void onConsentFormError(ConsentManagerException exception) { print($"ConsentFormListener - onConsentFormError, reason - {exception.getReason()}");}

public void onConsentFormOpened() { print("ConsentFormListener - onConsentFormOpened");}

public void onConsentFormClosed(Consent consent) { print($"ConsentFormListener - onConsentFormClosed, consentStatus - {consent.getStatus()}");} 

Step 3: Initializing the SDK with the consent object

To use the received consent parameter when initializing the Appodeal SDK, call the Appodeal initialize method and pass the value of consent

//use getConsent() after the onConsentInfoUpdated method was called
Consent consent = ConsentManager.getInstance.getConsent(); 
Appodeal.initialize(APP_KEY, adType, 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:

Appodeal.updateConsent(true);

If you don't want to use the Stack Content Manager SDK, you can use the old version to update the user's consent:

Appodeal.initialize(APP_KEY, adType, true);

You can use this method to provide the GDPR/CCPA user consent for ad networks in Appodeal SDK anywhere in your application. Appodeal SDK don't keep the GDPR/CCPA user consent between session, that means you should provide consent every time, otherwise, Appodeal SDK uses default or server value. By default consent value is true.

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.

// Get manager
ConsentManager consentManager = ConsentManager.getInstance();

// Set storage
consentManager.setStorage(ConsentManager.Storage.SHARED_PREFERENCE);

// Get storage
ConsentManager.Storage iabStorage = consentManager.getStorage();
  • You can register yourself as a vendor before request.  Should be called before request.
ParameterTypeDescription
nameStringDisplay name. Will be displayed in the consent window
bundleString Custom string to check consent result for the vendor
policyUrlStringPolicy URL
purposeIdsArray of integersiAB purposes ids array
featureIdsArray of integersiAB features ids array
legitimateInterestPurposeIdsArray of integersiAB leg int purposes ids array


// Get manager
ConsentManager consentManager = ConsentManager.getInstance();

// Create custom vendor
Vendor customVendor = new Vendor.Builder(
                    "Custom Vendor",
                    "customvendor",
                    "https://customvendor.com")
                .setPurposeIds(new List<int> {1, 2, 3})
                .setFeatureId(new List<int> {1, 2, 3})
                .setLegitimateInterestPurposeIds(new List<int> {1, 2, 3})
                .build();

// Set custom vendor
consentManager.setCustomVendor(customVendor);

// Get by bundle
consentManager.getCustomVendor("customvendor");
HasConsentDefinition
TRUEThe vendor's consent value is true.
FALSEThe vendor's consent value is false.
UNKNOWNThe value is undefined(the requestConsentInfoUpdate method was not called).

Best practices

Stack Consent Manager SDK update consent + Appodeal SDK initialize

ConsentManager consentManager = ConsentManager.getInstance();
consentManager.requestConsentInfoUpdate("YOUR_APP_KEY", this);

#region ConsentInfoUpdateListener
public void onConsentInfoUpdated(Consent consent)
{
  // Initialize the Appodeal SDK with the received Consent object here or show consent window
	if(consentManager.shouldShowConsentDialog()== Consent.ShouldShow.TRUE){
    	//Show consent window
    }else{		
       Appodeal.initialize(APP_KEY, adType, consent);
    } 
}

public void onFailedToUpdateConsentInfo(ConsentManagerException error)
{
	Debug.Log($"onFailedToUpdateConsentInfo Reason: {error.getReason()}");
    // User's consent status failed to update.
}
#endregion

Stack Consent Manager SDK load and show consent dialog + Appodeal SDK initialize

public void onConsentFormLoaded() 
{ print("ConsentFormListener - onConsentFormLoaded");}

public void onConsentFormError(ConsentManagerException exception) 
{ print($"ConsentFormListener - onConsentFormError, reason - {exception.getReason()}");}

public void onConsentFormOpened() 
{ print("ConsentFormListener - onConsentFormOpened");}

public void onConsentFormClosed(Consent consent) 
{ 
	print($"ConsentFormListener - onConsentFormClosed, consentStatus - {consent.getStatus()}");
	Appodeal.initialize("APP_KEY", adType, consent);
}