Consent Docs

Android SDK

Build the consent jar for an Android app, create the client and look up every method, type and exception.

Reference8 min readFor developers using the browser runtime and the server and mobile SDKs

PiedapConsent records consent from a native Android app and keeps the answer on the device. It draws no banner: your app shows its own screen from the form the server sends.

Requirements#

  • Plain Java 8 with no dependencies. Call it from Java or Kotlin.
  • A JDK, version 9 or later, to build the jar.
  • The android.permission.INTERNET permission in the app's manifest.
  • A publishable key, pk_…. Its Allowed origins must list app: followed by the application id, such as app:in.example.kalpa.
  • A published form that a region rule sends your users to.

A test key answers any app. A live key answers only the apps on its list. See Mobile apps.

Install#

The SDK is not published to a public registry or served by the deployment. Your account contact provides the source. Build the jar with the script in the consent-android folder:

Shell
./build.sh

The script compiles, runs the tests and writes build/piedap-consent-android-1.0.1.jar. Copy the jar into your app module's libs folder and add it to the build:

build.gradleGradle
dependencies {
    implementation files('libs/piedap-consent-android-1.0.1.jar')
}
build.gradle.ktsKotlin
dependencies {
    implementation(files("libs/piedap-consent-android-1.0.1.jar"))
}

Create the client#

PiedapConsent.builder()#

Builds the client. Create one and keep it for the life of the app.

Builder method Type Required or default Description
apiUrl() String Required The deployment address.
publishableKey() String Required The publishable key, pk_….
applicationId() String Required The application id. It must be the id listed on the key after app:.
store() ConsentStore MemoryConsentStore Where state is kept. The default forgets everything when the app exits.
timeoutMs() int 10000 Connect and read timeout in milliseconds.

Returns a PiedapConsent client from build().

Errors publishableKey() throws IllegalArgumentException for a key that does not start with pk_. build() throws IllegalStateException without an address, a key or an application id.

java
import com.piedap.consent.PiedapConsent;

PiedapConsent consent = PiedapConsent.builder()
    .apiUrl("https://console.dpconsent.com/consent")
    .publishableKey("pk_…")
    .applicationId(context.getPackageName())
    .store(new PreferencesStore(context))
    .build();

Each request carries the key in X-Piedap-Key and the application id in X-Piedap-App. Redirects are never followed. PreferencesStore is a class you add to the app. It is shown under ConsentStore.

Methods#

Network calls block, so run them off the main thread. There is no offline queue and no retry: a failed save throws, and the answer is not stored. Reading never needs the network.

loadForm()#

Fetches the form for this device.

Parameter Type Required or default Description
locale String Required A language tag, such as hi-IN.
countryCode String Required, may be null A two-letter country code. With null, the server works it out from the request.

Returns a ConsentForm. The country you pass picks the form only. The record takes its country and law from where the request comes from.

Errors Throws ConsentApiException.

java
ConsentForm form = consent.loadForm(
    Locale.getDefault().toLanguageTag(), null);
ConsentForm formForIndia = consent.loadForm("hi-IN", "IN");

shouldAsk()#

Tells whether to show your consent screen for a form.

Parameter Type Required or default Description
form ConsentForm Required The form from loadForm.

Returns boolean. It is true:

  • before the first decision
  • when the published form has changed since the user answered, unless its consentModel is NOTICE_ONLY
  • when the server asks for consent again

It is false when the form is not available.

Errors None.

java
if (consent.shouldAsk(form)) {
  presentConsentScreen(form);
}

acceptAll()#

Records that the user accepted every purpose. The record's interaction is ACCEPT_ALL.

Parameter Type Required or default Description
form ConsentForm Required The form the user answered.

Returns Map<String, Boolean>, the stored decisions by purpose key.

Errors Throws ConsentApiException. Throws IllegalStateException for a form that is not available.

java
consent.acceptAll(form);

rejectAll()#

Records that the user refused every optional purpose. The record's interaction is REJECT_ALL.

Parameter Type Required or default Description
form ConsentForm Required The form the user answered.

Returns Map<String, Boolean>, the stored decisions. Always-on purposes stay allowed.

Errors Throws ConsentApiException. Throws IllegalStateException for a form that is not available.

java
consent.rejectAll(form);

savePreferences()#

Records the user's own choices. The record's interaction is SAVE_PREFERENCES.

Parameter Type Required or default Description
form ConsentForm Required The form the user answered.
choices Map<String, Boolean> Required Purpose key to allowed. A purpose you leave out counts as declined.

Returns Map<String, Boolean>, the stored decisions. Always-on purposes stay allowed.

Errors Throws ConsentApiException. Throws IllegalStateException for a form that is not available.

java
Map<String, Boolean> choices = new HashMap<>();
choices.put("analytics", true);
choices.put("marketing", false);
consent.savePreferences(form, choices);

The three recording methods write records in the same way:

  • The first answer creates a record. A change of mind on the same form version updates it.
  • An answer to a new version, or to a request for fresh consent, creates a new record. So does a change to a record that was erased on the server.
  • The record is kept under a random id made for this install, with the type DEVICE_ID, unless you called identify.

granted()#

Tells whether the stored decision allows a purpose.

Parameter Type Required or default Description
purposeKey String Required The purpose key.

Returns boolean. It is false for every purpose until the user decides. After loadForm finds no form for the user's location, it is true for every purpose, unless a decision is already stored.

Errors None.

java
if (consent.granted("analytics")) {
  analytics.enable();
}

decisions()#

Returns the stored decisions by purpose key.

Takes no parameters.

Returns Map<String, Boolean>, which cannot be changed. It is empty until the user decides.

Errors None. A stored value that cannot be read counts as no decision.

java
Map<String, Boolean> decisions = consent.decisions();

consentId()#

Returns the id of the stored record.

Takes no parameters.

Returns String, cns_…, or null before the first answer.

Errors None.

java
String consentId = consent.consentId();

addListener()#

Adds a listener that is called with the decisions after every save and withdrawal.

Parameter Type Required or default Description
listener PiedapConsent.Listener Required Its onConsentChanged(Map<String, Boolean>) runs on the thread that made the call.

Returns nothing. The client takes several listeners.

Errors None.

java
consent.addListener(latest -> applyConsent(latest));

removeListener()#

Removes a listener added with addListener().

Parameter Type Required or default Description
listener PiedapConsent.Listener Required The listener to remove.

Returns nothing.

Errors None.

java
consent.removeListener(listener);

withdraw()#

Withdraws the record on the server and clears it from the device.

Takes no parameters.

Returns nothing. The next shouldAsk is true again.

Errors Throws ConsentApiException. The stored decision then stays as it was.

java
consent.withdraw();

identify()#

Records consent created from now on under the signed-in person instead of the device.

Parameter Type Required or default Description
subjectId String Required The person's identifier, exactly the one the proof was made for.
subjectType String Required USER_ID, EMAIL or PHONE.
proof String Required The identity proof your server computed: 64 hex characters.

Returns nothing. The identity is kept in memory only, so call identify after sign-in on every launch.

Errors Throws IllegalArgumentException for an empty id, another type, or a proof that is not 64 hex characters.

java
consent.identify(session.userId, "USER_ID", session.consentProof);

identify does not call the server. A proof the server cannot verify is refused when the next record is created, with status 403 and the code IDENTITY_PROOF_REQUIRED. A decision already recorded under the device id keeps being updated in its own record. Compute the proof with identityProof() or identity_proof().

clearIdentity()#

Records consent created from now on under the device again. Call it on sign-out.

Takes no parameters.

Returns nothing.

Errors None.

java
consent.clearIdentity();

Types#

ConsentForm#

The fields are public and final. A text field the form does not have is null.

Field Type Description
available boolean false when no form is targeted at the user's location. There is nothing to ask.
title, body String The wording of the notice, in the language asked for.
acceptAllLabel, rejectAllLabel, savePreferencesLabel String Button wording.
privacyPolicyUrl String The privacy policy link, if the form has one.
purposes List<Purpose> The purposes to show.
consentModel String OPT_IN, OPT_OUT or NOTICE_ONLY.
law String The law that applies, such as DPDP.
key, contentHash, locale String Which form this is, and the language of its wording.
version int The form's version.
reconsentRequired boolean true when the server asks for a fresh answer.

Purpose#

Field Type Description
key String The purpose key.
name, description, category String What to show for the purpose.
alwaysOn boolean The purpose cannot be switched off.
defaultGranted boolean The form's default for the purpose.

ConsentStore#

The interface the client keeps its state through.

Member Description
get(String key) Returns the stored string, or null.
put(String key, String value) Stores a string.
remove(String key) Removes a key.
MemoryConsentStore Built in. Keeps state in memory only.

To keep state between launches, add a store backed by SharedPreferences:

PreferencesStore.javajava
import android.content.Context;
import android.content.SharedPreferences;
import com.piedap.consent.ConsentStore;

final class PreferencesStore implements ConsentStore {
  private final SharedPreferences preferences;

  PreferencesStore(Context context) {
    preferences = context.getSharedPreferences(
        "piedap_consent", Context.MODE_PRIVATE);
  }

  @Override public String get(String key) {
    return preferences.getString(key, null);
  }

  @Override public void put(String key, String value) {
    preferences.edit().putString(key, value).apply();
  }

  @Override public void remove(String key) {
    preferences.edit().remove(key).apply();
  }
}

The store holds a random id for the install, the consent id, the latest decisions, the form and version they answer, and whether the last fetch found a form. Its keys start with piedap.consent..

Errors#

A failed request throws ConsentApiException, a checked exception.

Method Type Description
getStatus() int The HTTP status. 0 when the server was not reached.
getMessage() String The server's message.
getCode() String The server's code, or null.

Match on the status and the code, not on the message. For some errors, such as a 401, the message arrives as a list in brackets, such as [Unauthorized]. A 401 means the key was not accepted. It is wrong, revoked or turned off, or it is a live key and the application id is not in its allowed origins. Every code is listed in Errors.

A mistake the SDK finds before it makes a request throws an unchecked exception. A catch (ConsentApiException …) does not catch it.

Mistake Exception
A key that does not start with pk_ IllegalArgumentException
No address, key or application id IllegalStateException from build()
identify with an empty id, another type, or a proof that is not 64 hex characters IllegalArgumentException
An answer to a form that is not available IllegalStateException
java
try {
  consent.acceptAll(form);
} catch (ConsentApiException error) {
  Log.w("consent", error.getStatus() + " " + error.getCode());
}

Not in this SDK#

  • A ready-made consent screen. Draw your own from the form.
  • Choices partner by partner. The SDK answers per purpose only.
  • Linked identifiers. Link them from your server with linkIdentifiers().
  • An offline queue or retries.
  • React Native and Flutter wrappers. Bridge the native SDK yourself, or call the API with the key in X-Piedap-Key and your app id in X-Piedap-App.
  • The complaint link, the rights request link, the privacy contact email and the list of languages. Read form.copy.complaintUrl, runtime.rightsUrl, runtime.contactEmail and form.locales from GET /sdk/v1/form.
  • Tracker blocking, Google Consent Mode, IAB TCF and GPP. Gate your own SDKs on granted.

↑↓ to move, Enter to open