Skip to content

User Consent

Part of the Human Scoring use case.

The SDK records how the user interacts with your app, so by default it records nothing until the user agrees. Your app asks the question — with the SDK's ready-made dialog, or in its own UI — and the SDK stores the answer and enforces it.

When to use it

  • Always, unless your app already obtains consent before calling DeaSdk.init — in that case, set requireConsent = false
  • To let the user withdraw consent from a settings screen
  • To ask again, for example when a different account signs in

Prerequisites

  • The SDK is installed and initialized. See the Quick Start.

Usage

Ask with the built-in dialog

import ai.dea.sdk.DeaConsentDialog

// In an Activity, on the main thread:
DeaConsentDialog.show(this) { granted ->
    // The user's answer, or the answer already on file.
}
  • When an answer is already stored, the dialog is not shown: onResult is called at once with that answer. Put the call in front of a session unconditionally; it only interrupts the user the first time.
  • The dialog does not close on a tap outside it. The back button closes it and calls onResult with false, but stores nothing, so the user is asked again next time.
  • When the SDK is not initialized, show shows nothing and calls onResult(false). So does an activity that is finishing, when no answer is stored.
  • Pass ask = false to read the stored answer without ever showing the dialog.

The dialog's text is the ConsentCopy you pass. The default, in English, says what is recorded and what never is:

Field Default
title Record this session?
body This app measures how a real person moves, so that people can be told apart from bots.
recorded Where you touch, and how hard · How fast your finger travels · How the handset moves in your hand
notRecorded What you type · What's on your screen · Anything you say
closing Nothing is recorded until you agree, and you can stop at any time.
accept, decline I agree · Not now

The lists are claims about your app. If they are not true of it — for example because your app reports key presses with captureKey — pass your own text:

import ai.dea.sdk.ConsentCopy
import ai.dea.sdk.DeaConsentDialog

DeaConsentDialog.show(
    this,
    copy = ConsentCopy(
        title = "Help us keep bots out",
        notRecorded = listOf("What's on your screen", "Anything you say"),
    ),
) { granted -> }

An empty list drops that section of the dialog.

Ask in your own UI, then record the answer

import ai.dea.sdk.DeaSdk

val dea = DeaSdk.instance ?: return

if (!dea.consent.isAnswered) {
    // Show your prompt, then:
    dea.setConsent(true)  // the user agreed
    // or
    dea.setConsent(false) // the user declined
}
  • Recording starts or stops as soon as setConsent is called.
  • The answer is stored on the device, so isAnswered stays true on later launches and you only ask once.
  • A decline is an answer and is stored too. Do not ask again on every launch after a decline.

Read the current answer

val consent = DeaSdk.instance!!.consent

consent.isAnswered // false until the user has answered
consent.isGranted  // true only if the user agreed
consent.decision   // ConsentDecision.UNKNOWN, GRANTED or DENIED
DeaSdk.instance?.setConsent(false)

Recording stops immediately and anything captured but not yet processed is discarded. The session is kept, so if the user agrees again, recording continues in the same session.

Ask again

DeaSdk.instance?.consent?.forget()

forget() clears the stored answer, so isAnswered is false again and your app asks next time — for example when a different account signs in on the same device. Unlike declining, it is not an answer. Nothing is recorded until the user answers again.

Be notified when the answer changes

import ai.dea.base.ConsentDecision

val unsubscribe = DeaSdk.instance!!.consent.onChange { decision: ConsentDecision ->
    updateConsentSwitch(decision == ConsentDecision.GRANTED)
}

// Later, when you no longer need updates:
unsubscribe()

Shared devices

When a different person may use the device each session (a kiosk, a test device), do not store the answer. Pass a Consent that is kept for this launch only:

import ai.dea.base.Consent
import ai.dea.sdk.DeaConfig
import ai.dea.sdk.DeaSdk

DeaSdk.init(this, DeaConfig(appId = "YOUR_APP_ID", appKey = "YOUR_APP_KEY", consent = Consent()))

Or keep the default store and record each answer for this launch only:

import ai.dea.sdk.DeaSdk

DeaSdk.instance?.setConsent(true, remember = false)

The built-in dialog does the same with remember = false. It also clears the stored answer first, because that answer was given by whoever held the device before, and then asks on every call:

import ai.dea.sdk.DeaConsentDialog

DeaConsentDialog.show(this, remember = false) { granted -> }

If your own consent record already says yes, pass it in instead of asking again:

import ai.dea.base.Consent
import ai.dea.sdk.DeaConfig
import ai.dea.sdk.DeaSdk

DeaSdk.init(this, DeaConfig(appId = "YOUR_APP_ID", appKey = "YOUR_APP_KEY", consent = Consent.granted()))

To keep the answer in your own records instead of the SDK's, implement ConsentStore and pass Consent(store = yourStore). The answer is read from your store when you create the Consent, and every answer recorded with remember = true is written to it.

Examples

Ask on the first screen

import ai.dea.sdk.DeaSdk
import android.app.AlertDialog
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class HomeActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_home)

        val dea = DeaSdk.instance ?: return
        if (dea.consent.isAnswered) return

        AlertDialog.Builder(this)
            .setTitle("Help us keep bots out")
            .setMessage(
                "We measure how you touch the screen and how the phone moves in your hand, to " +
                    "tell people apart from bots. We never record what you type or what is on " +
                    "your screen. You can change this at any time in Settings.",
            )
            .setCancelable(false)
            .setPositiveButton("Allow") { _, _ -> dea.setConsent(true) }
            .setNegativeButton("Not now") { _, _ -> dea.setConsent(false) }
            .show()
    }
}

Make sure the wording stays true for your app.

A settings switch

import ai.dea.sdk.DeaSdk
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.SwitchCompat

class SettingsActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_settings)

        val dea = DeaSdk.instance
        val toggle = findViewById<SwitchCompat>(R.id.bot_protection)

        toggle.isEnabled = dea != null
        toggle.isChecked = dea?.consent?.isGranted ?: false
        toggle.setOnCheckedChangeListener { _, checked -> dea?.setConsent(checked) }
    }
}

API

Member Returns Description
DeaConsentDialog.show(activity, sdk, copy, remember, ask, onResult) Unit Shows the built-in dialog unless an answer is stored, records the answer, and calls onResult
DeaSdk.setConsent(granted, remember = true) Unit Records the answer and applies it at once; remember = false keeps it for this launch only
DeaSdk.consent Consent The current answer
Consent.isAnswered Boolean Whether the user has answered
Consent.isGranted Boolean Whether the user agreed
Consent.decision ConsentDecision UNKNOWN, GRANTED or DENIED
Consent.forget() Unit Clears the stored answer
Consent.onChange(listener) () -> Unit Calls listener when the answer changes; returns the unsubscribe
Consent() Consent An answer kept for this launch only
Consent.granted() Consent An answer that is already yes
Consent(store) Consent An answer kept in your own ConsentStore