Info
Content

[Android] 1. consentmanager SDK Integration

On this document, you'll find general information on how to integrate our SDK to your project. For further details, please refer to our API Reference documentation.

Since the version 1.7.0, our SDK repository has been moved to the official Maven repository. The migration Guide can be found here. Find the compatible native SDK Versions here.

1. Installation

The consentmanager SDK for Android apps implements and provides functionality to inform the user about data protection and ask and collect consent from the user. It enables app-developers to easily integrate the consentmanager service into their app.

Steps - High Level

    1. Integration and Configuration:

      • Integrate the SDK into your app.
      • Configure the SDK settings according to your needs.
    2. Creating an Instance:

      • On app startup, create an instance of the CMPConsentTool class. This instance will handle the consent process.
    3. SDK Initialization:
      • Once the instance is ready, the SDK automatically retrieves necessary information from the consentmanager servers to prepare for its operation.
    4. Displaying the Consent Screen:

      • The SDK will automatically display the consent screen if needed when the CMPConsentTool instance is created.
    5. Processing Personal Data:

      • Once consents are collected, info is stored and is available for querying through different properties and methods exposed by our SDK. You'll have information about rejected or accepted consents, vendors, purposes, etc.

    By following these steps, you ensure that your app is compliant with consent requirements and that user consent is properly managed and stored.

Consent Manager Provider SDK Sequence Diagram

To illustrate the steps above, let's check in the diagram below three possible SDK sequence flows. 

1. When creating an instance using the initialize function, there are two possible outcomes. The first is when the consentmanger API informs the SDK that the CMP will not open, which triggers the OnCmpNotOpenedCallback. The second outcome is when the consent layer opens, allowing the user to interact with it, and this triggers the OnOpenCallback. Once the user gives consent and the consent is processed, the OnCmpCloseCallback is called.

Please note that the OnErrorCallback is represented by the red dashed arrow lines to provide examples of when errors may occur during the process.

Initialize-Cmp-Sequence-Diagram.png

2. Creating an instance and calling the openAndCheckConsent functions will lead to a similar process. The difference is that by decoupling the creation of the instance and the check for the consentmanger API, you gain the ability to add business logic and interact with the libraries API.

3. Creating an instance and calling the openLayer function will open the layer without checking the consentmanager, if it's necessary. If there is already given consent, the options and settings will be shown to the user. The process flow will look like this:

openlayer-Cmp-Sequence-Diagram-.png

For further information about our SDK Version Overview & Changelog, please refer to this link.

Adding dependency via Gradle

Add the dependency to your apps build.gradle. (To always get the latest version use the + symbol to get the newest updates. You can for example always get the newest versions for minor updates through 1.x.+)

dependencies {
  implementation 'net.consentmanager.sdk:android:x.xx.x'
}

I

Adding dependency via Maven

Add the dependency to your apps build.gradle. To always get the latest version in maven you can use different methods to decline the version range. To check the available versions of the SDK, please check this link. For further information, please refer here.

<dependency>
    <groupId>net.consentmanager.sdk</groupId>
    <artifactId>android</artifactId>
    <version>x.xx.x</version>
</dependency>

2. Initializing the SDK

Permissions

This SDK requires the following permissions, please ensure to add them to your AndroidManifest.xml:

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />

Initializing the ConsentTool - Automatically

Within the app-start (usually the regular override onCreate() function) , you must create an instance of class CMPConsentTool.  The initialize() function will automatically fetch the necessary data from our server and determine if the consent screen needs to be shown or not. If so, the SDK will automatically show the consent screen at this point, collect the data and provide the data to the app. The instance can then be used in order to get consent details from the SDK in order to use it in the app.Example of initialization, using the automatic behaviour of the initialize() method:

To initialize the ConsentTool, go to your targeted class and create a instance of CMPConsentTool like shown below:

// Kotlin example of initialization of the consent layer
class CmpDemoActivity : FragmentActivity() {

    private lateinit var cmpManager: CmpManager

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        val config = CmpConfig.apply {
            id = "<YOUR-CONSENTMANAGER-APP-ID>" // example: b238acdf1a
            domain = "<YOUR-CONSENTMANAGER-APP-DOMAIN>" // example: delivery.consentmanager.net
            appName = "<YOUR-CONSENTMANAGER-APP-NAME>" // example: testApp
            language = "<YOUR-CONSENTMANAGER-APP-LANGUAGE>" // example: DE
        }
        
        cmpManager = CmpManager.createInstance(this, config)
        cmpManager.initialize(this)
    }
}
// Java example of initialization of the consent layer
public class CmpDemoActivity extends AppCompatActivity {

    private CmpManager cmpManager;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        CmpConfig cmpConfig = CmpConfig.INSTANCE;
        cmpConfig.setId("<YOUR-CONSENTMANAGER-APP-ID>"); // example: a000aaaa1a
        cmpConfig.setDomain("<YOUR-CONSENTMANAGER-APP-DOMAIN>"); // example: delivery.consentmanager.net
        cmpConfig.setAppName("<YOUR-CONSENTMANAGER-APP-NAME>"); // example: testApp
        cmpConfig.setLanguage("<YOUR-CONSENTMANAGER-APP-LANGUAGE>"); // example: EN
        cmpConfig.setTimeout(4000);

        cmpManager = CmpManager.createInstance(this, cmpConfig);
    }

    // ... open layer and ask for purpose

    private void openConsentLayerAndCheckPurpose() {
        cmpManager.openConsentLayer(getApplication());

        if (cmpManager.hasPurposeConsent("PURPOSE_ID")) {
            Log.d("CmpDemoActivity", "Has purpose consent");
        } else {
            Log.d("CmpDemoActivity", "Does not have purpose consent");
        }
    }
}

In order to create the instance of CMPConsentTool you need to configure the instance. You will need to provide the CODE-ID, server domain , an app name and a language. The CODE-ID and server domain can be found in your consentmanager account under Menu > Get Code. The app name can be used in order to distinguis different apps in the consentmanager reporting. For the language you can either use an empty string ("") for auto-detection or a 2-letter language code ("EN", "DE", "FR" and so on).

Initializing the ConsentTool - Manually

The SDK offers, for the sake of flexibility, a way to manually display the consent layer, as demonstrated below: 

        cmpManager.check({ isConsentRequired ->
            if (isConsentRequired) {
                // Consent is required, handle accordingly
                runOnUiThread {
                    // Update UI or show consent dialog
                    cmpManager.openLayer()
                }
            } else {
                // Consent is not required, proceed with application logic
            }
        }, isCached = true)

Creating a custom layout

For creating a custom layout you can use the CmpUIConfig class with different styling options. This class offers also some preset layouts like 

  • configureHalfScreenBottom
  • configureHalfScreenTop
  • configureCenterScreen
  • configureSmallCenterScreen
  • configureLargeTopScreen
  • configureLargeBottomScreen

For creating a custom layout the CMP SDK offers also different strategies:

  • Dialog Window
  • Popup Window
  • Fragment

You can change the strategy by setting the UIConfig parameter: 

CmpUIConfig.uiStrategy = CmpUIStrategy.DIALOG
CmpUIConfig.uiStrategy = CmpUIStrategy.POPUP
CmpUIConfig.uiStrategy = CmpUIStrategy.ACTIVITY
CmpUIConfig.uiStrategy = CmpUIStrategy.FRAGMENT

We recommend to use a Popup Window which is also set as default since version 2.3.0

Two important parameters will determine the Popup and Dialog behaviour. In the table below, you can check the relation between the parameters isFocusable and isOutsideTouchable

Popup behaviour
Parameter isFocusable = true isFocusable = false
isOutsideTouchable =  true Dismisses on outside touch. Can gain focus for input events. Dismisses on outside touch. Does not gain focus or intercept keyboard input.
isOutsideTouchable = false Does not dismiss on outside touch. Can gain focus and intercept input events. Does not dismiss on outside touch. Does not gain focus or intercept keyboard input.

 

 

 

Dialog behaviour
Parameter isFocusable = true isFocusable = false
isOutsideTouchable =  true Dismisses on outside touch (setCanceledOnTouchOutside(true)). Dialog is focusable by default. Dialog does not dismiss on outside touch and might not behave as expected since dialogs are typically focusable.
isOutsideTouchable = false Does not dismiss on outside touch (setCanceledOnTouchOutside(false)). Dialog remains focusable and can intercept input events. Dialog does not dismiss on outside touch and might not behave as expected due to the lack of focusability.
Using the Fragment strategy
R.id.cmpContainer is a Framelayout which could look like this in the activity layout xml in layout/{your_activity}.xml

<FrameLayout
    android:id="@+id/cmpContainer"
    android:layout_width="match_parent"
    android:layout_height="400dp"
    android:translationZ="90dp"
    app:layout_constraintTop_toTopOf="parent" />

3. Using the SDK

In order to check whether a vendor or purpose have consent, you can use the two methods:

if (cmpManager.hasPurposeConsent("52")) {
    if (cmpManager.hasVendorConsent("s26")) {
        // Add your logic here
    }
}

Both methods hasPurposeConsent and hasVendorConsent have two parameters, one require and one optional:

  • id - String of the vendor or purpose ID. Please note that vendor IDs can have different formats ("123", "s123" and "c123"), please double-check with Menu > Vendors and Menu > Purposes in your consentmanager account.
  • isIABVendor / isIABPurpose (Optional) - If the vendor or purpose is a vendor/purpose that follows the IAB TCF standard, you will need to set a true, otherwise a false.

Remember: All vendor that do not belong to the IAB have IDs starting with a "s" or "c" (e.g. "s123"); vendors that belong to the IAB have IDs not starting with a "s" or "c".

Re-Opening the Consent Screen

In order to allow the user to change the choices, you can simply call openConsentLayer():

cmpManager?.openConsentLayer(context)

In some cases a native app might contain webviews in order to display certain things like advertising oder content. In order to transmit the consent information from the SDK to the webview, please use the function:

String consentData = cmpConsentTool?.exportCmpString();

This will export the consent information and all further data that is needed by the CMP. You can then pass this information to the CMP that is in your webview by adding it to the URL that is called in the webview:

myWebView.loadURL("https://mywebsite.com/....#cmpimport=" + consentData);

Custom Event Listeners

To add additional process logic you can make use of Event Listeners. Following Event Listeners are available:

Name

Occurrs

 

OnOpenCallback

Listener for Event when CMP opened

OnCMPCloseCallback

Listener for Event when CMP is closed

OnCMPNotOpenedCallback

Listener for Event when CMP doesn't need to be opened

OnErrorCallback

Listener for Event when there is an error in the consent management process.

OnButtonClickedCallback

Listener for ButtonEvent

Import/Export Consent

To import or export the consent you can use the functions exportCMPData() and importCMPData(). Check the example below: 

The consentString you need to pass should be base64 encoded.

To implement the functionality where certain domains are whitelisted and, when accessed within the consent platform (CMP) WebView, are not opened in an external browser like Chrome but within the WebView itself, you can implement a callback mechanism to perform custom actions based on the domain, such as opening an Android activity.

// apply the domains to be whitelisted
CmpConfig.apply {
            id = cmpId
            domain = cmpDomain
            appName = cmpAppName
            language = cmpLanguage
            domainWhitelist = cmpDomainWhitelist
        }


// implement the callback: CmpOnClickLinkCallback
    override fun onClickLink(url: String): Boolean {
        Log.d("CMP", url)
        // Business logic
        return true // return handleWebViewInteraction boolean
    }

Logging

When using our Android SDK, you may need to debug or analyze log information for various purposes. The logs generated by our SDK are tagged with "CMP", which allows you to easily filter and view only the relevant logs. This guide provides step-by-step instructions on how to access these logs using Logcat in Android Studio.

Search for the Tag: In the search bar above the log statements, type CMP to filter out the logs tagged with "CMP".

Optional: Enable Debug Mode

In CMPConfig, set isDebugMode = true.

val config = CmpConfig.apply {
    // ... other settings
    isDebugMode = true
}
  • Enables more detailed logs tagged with "CMP".
  • Useful for debugging and analysis.

Troubleshooting

Class Not Found or NoSuchMethodException:

ProGuard can sometimes obfuscate class names or remove methods that are dynamically referenced via reflection. To fix this, you need to specify the classes and methods that should be kept intact in the ProGuard configuration file using the -keep directive.

Example ProGuard configuration to keep a specific class and its methods:

# Kotlin serialization looks up the generated serializer classes through a function on companion
# objects. The companions are looked up reflectively so we need to explicitly keep these functions.
-keepclasseswithmembers class **.*$Companion {
    kotlinx.serialization.KSerializer serializer(...);
}
# If a companion has the serializer function, keep the companion field on the original type so that
# the reflective lookup succeeds.
-if class **.*$Companion {
  kotlinx.serialization.KSerializer serializer(...);
}
-keepclassmembers class <1>.<2> {
  <1>.<2>$Companion Companion;
}

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}

-keepattributes JavascriptInterface

-keepclassmembers class net.consentmanager.sdk.common.callbacks.* {
   public *;
}

-keepclassmembers class net.consentmanager.sdk.consentlayer.ui.consentLayer.CmpWebView {
   public *;
}

-keepclassmembers class net.consentmanager.sdk.consentlayer.ui.CmpLayerAppInterface {
   public *;
}
-keep class net.consentmanager.sdk.CMPConsentTool {
                                                      *;
                                                  }

-keepclassmembers class * {
    @android.webkit.JavascriptInterface <methods>;
}

-keepattributes JavascriptInterface

# Serializer for classes with named companion objects are retrieved using `getDeclaredClasses`.
# If you have any, uncomment and replace classes with those containing named companion objects.
#-keepattributes InnerClasses # Needed for `getDeclaredClasses`.
#-if @kotlinx.serialization.Serializable class
#com.example.myapplication.HasNamedCompanion, # <-- List serializable classes with named companions.
#com.example.myapplication.HasNamedCompanion2
#{
#    static **$* *;
#}
#-keepnames class <1>$$serializer { # -keepnames suffices; class is kept when serializer() is kept.
#    static <1>$$serializer INSTANCE;
#}

 

Back to top