Android SDK
Installation
The minSdkVersion
version number in your build.gradle
should not be less than 21
.
To add the SDK to your app, please follow the following steps:
Add this dependency to your project’s
build.gradle
orsettings.gradle
under theallprojects
block
GroovyGROOVYallprojects { repositories { maven { url 'https://jitpack.io' } } }
Kotlin DSL
KOTLINallprojects { repositories { maven (url = uri("https://jitpack.io")) } }
Add this dependency
com.github.Utiq-tech.UTIQ-Mobile-SDK:utiq:{VERSION}
to the.gralde
file of your App or module.
You can get the latest version from here.
Also, add the following permissions to your manifest file
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
Initialize the SDK
Initializing the SDK
Basic initializing
Assuming that you have an Application class registered in the AndroidManifest.xml
file, you can call the initialize function inside onCreate()
, passing the application context and the SDK token that you will get from us.
Kotlin
UTIQ.initialize(this, SDK_TOKEN)
Java
UTIQ.initialize(this, SDK_TOKEN);
Please email us for a new token and a config file for your mobile App.
Initializing with custom options
UTIQOptions()
is an optional initialization parameter, so you can initialize the SDK without passing it, but in case there are options that you want to enable or disable specifically, this can be done by enabling or disabling the options in theUTIQOption
and passing it to the initializer.
Currently, enable debugging and the fallback config are the only available options.
Kotlin
val fallbackConfigJson : String = "{ /* fallbackConfigJson */ }"
val options = UTIQOptions().enableLogging().setFallBackConfigJson(fallbackConfigJson)
UTIQ.initialize(this, SDK_TOKEN, options)
Java
String fallbackConfigJson = "{ /* fallbackConfigJson */ }";
UTIQOptions options = new UTIQOptions().enableLogging().setFallBackConfigJson(fallbackConfigJson);
UTIQ.initialize(this, SDK_TOKEN, options);
Check if the SDK is initialized
At some point, you might want to check if the SDK is initialized before doing anything else.
Kotlin
UTIQ.isInitialized()
Java
UTIQ.isInitialized();
onInitialize listener
You would need to do this if you want to do something with the SDK very early (for example in the splash screen) and you want to make sure that the SDK is initialized before doing anything with the SDK, however if you will use the SDK functions in a class that will require going through multiple steps, such as the cart page in an e-commerce app, most probably you won’t need to use this function because the SDK will be already initialized by the time of reaching to that page or class, so it is your call to decide when to use that check and when not to.
Kotlin
UTIQ.onInitialize({
// Success Action
}) {
// Failure Action
}
Java
UTIQ.onInitialize(() -> {
// Success Action
}, error -> {
// Failure Action
});
Basic usage
Once the SDK is initialized, all the functions can be called by calling UTIQ.
to access the SDK functions.
Integration
Fetch Utiq data
Kotlin
UTIQ.fetchIdConnectData(dataCallback = {
println("marTechPass: " + it.marTechPass + "adTechPass: " + it.adTechPass)
}, errorCallback = {
error.printStackTrace()
})
Java
UTIQ.fetchIdConnectData(null, idcData -> {
System.out.println("adTechPass: " + idcData.adTechPass + "marTechPass: " + idcData.marTechPass)
} , error -> {
error.printStackTrace();
});
You can use a stub token to test the Utiq service if you don’t have an eligible SIM card from one of the supported Telcos.
Please email us to generate a new stub token for your App.
An optional parameter, false by default.
Kotlin
UTIQ.fetchIdConnectData(STUB_TOKEN, dataCallback = {
println("marTechPass: " + it.marTechPass + "adTechPass: " + it.adTechPass)
}, errorCallback = {
error.printStackTrace()
})
Java
UTIQ.fetchIdConnectData(STUB_TOKEN, idcData -> {
System.out.println("adTechPass: " + idcData.adTechPass + "marTechPass: " + idcData.marTechPass)
} , error -> {
error.printStackTrace();
});
Utiq Consent
Dedicated guidelines for Utiq Privacy Requirements and Consent Experience on Mobile App can be found at this page: Consent Experience on Mobile App
The Utiq services require the user’s consent, hence it is crucial to show a prompt to the user to ask for his consent as without consent acceptance, Utiq data will not be fetched.
The SDK does not provide any function to show a consent alert/popup, so you should provide a custom popup that matches the look and feel of your App.
This consent popup should be shown to the user before calling the fetchIdConnectData()
function (Only if was not shown before) to ask for the user’s consent and pass the user's choice to the SDK.
While you can use the pop-up that matches the look and feel of your App, the consent description should follow our guidelines which can be found here.
Is consent accepted
This function returns a boolean indicating the consent status of the user (accepted or rejected).
Kotlin
UTIQ.isConsentAccepted()
Java
UTIQ.isConsentAccepted();
You can use this function to check the user’s consent before showing a dialog asking for the user’s consent.
Accept consent
Kotlin
UTIQ.acceptConsent()
Java
UTIQ.acceptConsent();
Reject consent
Kotlin
UTIQ.rejectConsent()
Java
UTIQ.rejectConsent(null, null);
With this function, we can reject the user’s consent, if a user changes his mind or if we want to reset the user’s consent.
Because this is an API call, you might want to do something when the request returns either success or failure, so you can use the same function above but with success and failure closures.
Kotlin
UTIQ.rejectConsent(successCallback = { ... }, errorCallback = { ... })
Java
UTIQ.rejectConsent(() -> { ... }, error -> { ... });
It is mandatory to prompt the user that his consent has been rejected successfully if it was accepted before and the prompt should adhere to the text in the guidelines.
consenthub URL
If there is a need for accessing the consenthub URL from within the mobile App, i.e. opening it in a web view, this can be done by calling the function that returns the consenthub URL.
Kotlin
UTIQ.getConsentHubUrl()
Java
UTIQ.getConsentHubUrl();
In the implementation phase, you might need to pass the stub token to the getConsentHubUrl(STUB_TOKENtoken)
function if you are not using an eligible SIM.
User Eligibility
Before asking for the user consent and fetching IdConnect data, it is good to check if the user is on a supported Telco or not by using the following function, doing this will avoid unnecessary API calls, and consent pop-up but in all cases Utiq won’t return any data if the Telco is not supported.
Kotlin
UTIQ.checkMNOEligibility({
// Success
}, {
error.printStackTrace()
})
Java
UTIQ.checkMNOEligibility(() -> {
// Success
return null;
}, error -> {
error.printStackTrace();
});
The eligibility check function also accepts a stub token so you can test freely without a supported SIM.
Clear cached data
Data and cookies cached locally can be cleared easily using the following functions.
Kotlin
UTIQ.clearData()
UTIQ.clearCookies()
Java
UTIQ.clearData();
UTIQ.clearCookies();
This does not delete data and cookies from our back-end, but only from the local storage of the mobile, to delete the user’s data, you should reject the function to reject the user's consent.
Error handling
All the functions with callbacks mentioned in the previous sections have success and failure callbacks/closures, the failure callback/closure returns a custom error relevant to the function, this custom error can be used to show an error or an action that can be taken based on the returned error.
All of the following errors are of type UtiqError
that inherits from Throwable
.
Showing errors flagged from the SDK to the end user is not recommended.
Errors are returned so an action can be taken by developers based on the error type.
Error | Description |
---|---|
| Denotes an HTTP exception with an error message and a status code. |
| Denotes that the internet is not reachable through WiFi or cellular. |
| This error will be thrown on an attempt to initialize the SDK after it has already been initialized. |
| This error will be thrown if you pass an empty token to the SDK’s initializer. |
| This error will be thrown if you try to access any function from the SDK before or without initializing it. |
| An invalid token was used to initialize the SDK. |
| Denotes that SDK config is not found. |
| When initiating the Utiq flow, the user status should be only one of these: |
| Indicates that a user has frozen Utiq from the consenthub for one year. |
| Denotes that the |
| An invalid token stub was used to initialize Utiq SDK. |
| The SIM card operator cannot be used with Utiq. |
| A user has not given his consent or that user was not asked to accept or reject consent. |
| The user has wiped his data from the consenthub, in this case, you have to clear the cached data and ask for the user’s consent then fetch IdConnect data again. |
| Denotes that the SIM operator is of use-case that is unknown to Utiq. |
| Denotes that mobile MNO URL was not found. |
| Denotes that data value was not found. |
| Denotes that data domain was not found. |
| Denotes a Telco with a token use-case, but not one of the Utiq-enabled Telcos. |
| SAML use-case Telco with an invalid session ID. |
| SAML use-case Telco with an invalid location URL. |
| SAML use-case Telco with an invalid authentication URL. |
| fetchIdConnectData function was not called. |
| Denotes that the ID connect API host was not found. |
| Denotes that ID connect data was not found. |
| Denotes an error that is not one of the errors in this table. |
| Denotes an unknown error. |
Example CMP agnostic integration (Didomi)
The following is a sample integration, feel free to organize the code the way that suits you, but please make sure to follow the general guidelines.
Create a class that has all the functions needed from Didomi, it can be a singleton class or an Interface and implementation that you can inject using any DI framework or service locator.
KOTLINobject DidomiSdk { private var isUtiqVendorEnabled = false private var isUtiqPurposeEnabled = false private val didomi = Didomi.getInstance() private lateinit var didomiEventListener: EventListener init { this.didomi.setLogLevel(Log.VERBOSE) } fun initialize(application: UtiqApplication) { /* The SDK will automatically use the remote configuration hosted by Didomi and cache it locally. The cached version is refreshed every 60 minutes. Config file example { "app": { "name": "My App Name", "privacyPolicyURL": "http://www.website.com/privacy", "vendors": { "iab": { "all": true } }, "gdprAppliesGlobally": true, "gdprAppliesWhenUnknown": true } } */ val initializeParameters = DidomiInitializeParameters( apiKey = YOUR_API_KEY_GOES_HERE, null, null, null, false, null, noticeId = YOUR_NOTICE_ID_GOES_HER ) this.didomi.initialize(application, initializeParameters) this.onInitialized { val currentUserStatus = this.didomi.currentUserStatus this.isUtiqVendorEnabled = currentUserStatus.vendors.entries.first { it.key.contains("utiq", false) }.value.enabled this.isUtiqPurposeEnabled = currentUserStatus.purposes.entries.first { it.key.contains("utiq", false) }.value.enabled } this.onError { Log.e("DemoApp", "Error while initializing Didomi SDK") } } fun startIfNeeded(activity: FragmentActivity, forceStart: Boolean) { this.onInitialized { if (forceStart) this.didomi.forceShowNotice(activity) else this.didomi.setupUI(activity) } } fun startedBefore() = !this.didomi.shouldUserStatusBeCollected() fun isUtiqEnabled() = this.isUtiqVendorEnabled && this.isUtiqPurposeEnabled fun reset() { this.didomi.reset() } fun resetUtiq() { onInitialized { val currentUserStatus = this.didomi.currentUserStatus /* If you want to hardcode the ID, The vendor ID can also be found in the Didomi's console in the Data Manager section, select the VENDORS tab, then search for the vendor you want to enable or disable, and the APP ID is the vendor ID. */ val utiqVendorId = currentUserStatus.vendors.keys.first { it.contains("utiq") } /* If you want to hardcode the ID, The purpose ID can also be found in the Didomi's console in the Data Manager section, select the Purposes tab, then search for the purpose you want to enable or disable and the APP ID is the purpose ID. */ val utiqPurposeId = currentUserStatus.purposes.keys.first { it.contains("utiq") } this.didomi.openCurrentUserStatusTransaction() .disableVendor(utiqVendorId) .disablePurpose(utiqPurposeId) .commit() } } fun onConsentStatusChange(action: (enabled: Boolean) -> Unit) { /* Listen for changes on the user status linked to a specific vendor. We always need to listen for changes from Didomi as the user might open the screen from another place and reject his consent that he granted before, in this case we need to keep the synchronization between Didomi and Utiq */ this.onInitialized { if (!::didomiEventListener.isInitialized) { this.didomiEventListener = this.createDidomiEventListener(action) this.didomi.addEventListener(this.didomiEventListener) } } } fun onError(errorAction: (errorMessage: String) -> Unit) { this.didomi.addEventListener(object : EventListener() { override fun error(event: ErrorEvent) { super.error(event) errorAction(event.errorMessage!!) } }) } fun onInitialized(action: () -> Unit) { this.didomi.onReady(action) } private fun createDidomiEventListener(action: (enabled: Boolean) -> Unit) = object : EventListener() { override fun preferencesClickVendorAgree(event: PreferencesClickVendorAgreeEvent) { super.preferencesClickVendorAgree(event) if (event.vendorId.contains("utiq", true)) isUtiqVendorEnabled = true } override fun preferencesClickVendorDisagree(event: PreferencesClickVendorDisagreeEvent) { super.preferencesClickVendorDisagree(event) if (event.vendorId.contains("utiq", true)) isUtiqVendorEnabled = false } override fun preferencesClickAgreeToAllVendors(event: PreferencesClickAgreeToAllVendorsEvent) { super.preferencesClickAgreeToAllVendors(event) isUtiqVendorEnabled = true } override fun preferencesClickDisagreeToAllVendors(event: PreferencesClickDisagreeToAllVendorsEvent) { super.preferencesClickDisagreeToAllVendors(event) isUtiqVendorEnabled = false } // This will be called when the agree selector switch one of the options of the second layer is selected override fun preferencesClickPurposeAgree(event: PreferencesClickPurposeAgreeEvent) { super.preferencesClickPurposeAgree(event) if (event.purposeId.contains("utiq", true)) isUtiqPurposeEnabled = true } // This will be called when the disagree selector switch one of the options of the second layer is selected override fun preferencesClickPurposeDisagree(event: PreferencesClickPurposeDisagreeEvent) { super.preferencesClickPurposeDisagree(event) if (event.purposeId.contains("utiq", true)) isUtiqPurposeEnabled = false } // This will be called when the agree all selector switch one of the options of the second layer is selected override fun preferencesClickAgreeToAllPurposes(event: PreferencesClickAgreeToAllPurposesEvent) { super.preferencesClickAgreeToAllPurposes(event) isUtiqPurposeEnabled = true } // This will be called when the disagree all selector switch one of the options of the second layer is selected override fun preferencesClickDisagreeToAllPurposes(event: PreferencesClickDisagreeToAllPurposesEvent) { super.preferencesClickDisagreeToAllPurposes(event) isUtiqPurposeEnabled = false } override fun preferencesClickSaveChoices(event: PreferencesClickSaveChoicesEvent) { super.preferencesClickSaveChoices(event) action(isUtiqEnabled()) } // This will be called when Agree of the first layer is clicked override fun noticeClickAgree(event: NoticeClickAgreeEvent) { super.noticeClickAgree(event) isUtiqVendorEnabled = true isUtiqPurposeEnabled = true action(isUtiqEnabled()) } // This will be called when Disagree of the first layer is clicked override fun noticeClickDisagree(event: NoticeClickDisagreeEvent) { super.noticeClickDisagree(event) isUtiqVendorEnabled = false isUtiqPurposeEnabled = false action(isUtiqEnabled()) } } }
Create a function to start and observe Didomi's status
KOTLINprivate fun startDidomiAndObserveConsentStatus(forceStart: Boolean) { DidomiSdk.apply { startIfNeeded(requireActivity(), forceStart) onConsentStatusChange { accepted -> if (accepted) { Utiq.clearData() // Only use stub if you are testing and do not have an eligible SIM card stubToken = YOUR_STUB_TOKEN_GOES_HERE Utiq.acceptConsent() fetchUtiqIds() } else { rejectUtiqConsent() // Do any further actions if needed } } } }
To synchronize Didomi's status with Utiq you can do the following
KOTLINDidomiSdk.onInitialized { if (DidomiSdk.startedBefore()) { if (DidomiSdk.isUtiqEnabled()) { // Only use stub if you are testing and do not have an eligible SIM card stubToken = YOUR_STUB_TOKEN_GOES_HERE Utiq.acceptConsent() fetchUtiqIds() } else // Didomi Consent was rejected before, Utiq will not start! } else startDidomiAndObserveConsentStatus(false) }
Then fetch Utiq IDs
KOTLINUtiq.run { fetchIdConnectData(stubToken, { // Do Whatever you want with the IDs }, { if (it is UserOptedOutFromUtiqException || it is UserFrozenUtiqForOneYearException) DidomiSdk.resetUtiq() // Handle the error }) }
Synchronize Didomi when Utiq is rejected manually from the App, i.e. from Manage Utiq page
KOTLINprivate fun rejectUtiqConsent(postAction: () -> Unit = {}) { Utiq.rejectConsent({ Utiq.apply { clearData() postAction() } }, { // Handle the error }) }
To Update Didomi’s status, you call this function
KOTLINthis.startDidomiAndObserveConsentStatus(true)
Support and bug reporting
If you have any suggestions or you want to report a bug or ask a question, please create a new issue in this repository.
You can also check if there is a similar issue to yours that has been reported and solved before.
Contact Utiq Customer Success team at csm@utiq.com for any inquiries.