> For the complete documentation index, see [llms.txt](https://cmp.docs.sirdata.net/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://cmp.docs.sirdata.net/applications-mobiles-sdk-natif/mobile-sdk/setup.md).

# Android — Installation

Cette page mène une application Android de l'absence de gestion du consentement jusqu'à un build de release avec Sirdata CMP, une étape à la fois : prérequis, installation, initialisation, interface de consentement, lecture du consentement, point d'entrée dans les réglages, utilisateurs américains, tests, build de release et montée de version depuis 1.2.0.

## 1. Prérequis

|                        | Minimum                                                                                      |
| ---------------------- | -------------------------------------------------------------------------------------------- |
| `minSdk`               | 23 (Android 6.0)                                                                             |
| `compileSdk`           | 34                                                                                           |
| JDK qui exécute Gradle | 17                                                                                           |
| Android Gradle Plugin  | 8.2.2                                                                                        |
| Kotlin                 | 1.9.22                                                                                       |
| Activité hôte          | une `ComponentActivity` — `AppCompatActivity` et `FragmentActivity` en sont des sous-classes |

* **Le `compileSdk` 34 vient des dépendances AndroidX du SDK.** `androidx.activity` 1.8.2, `androidx.core` 1.12.0 et Compose Material 3 1.2.0 déclarent 34 comme SDK de compilation minimal, et l'Android Gradle Plugin arrête le build d'une application qui compile en dessous.
* **Outils de build.** La version 2.0.0 a été vérifiée avec l'Android Gradle Plugin 8.2.2 et Kotlin 1.9.22, dans un build de debug et dans un build de release minifié ; les extraits de cette page en Views compilent aussi avec Kotlin 2.2.0. Le cœur partagé (`shared-android`) est compilé en bytecode Java 21, que le D8 et le R8 de votre build doivent accepter : c'est le cas d'AGP 8.2.2, et les versions antérieures n'ont pas été testées. Votre propre module peut continuer à cibler Java 17.
* **Votre application n'a pas besoin de Jetpack Compose.** L'interface de consentement est écrite en Compose, mais le SDK apporte ses propres dépendances Compose : une application construite avec des Views fonctionne sans changement.

**Votre application a besoin d'`androidx.activity` et des coroutines kotlinx dans son classpath de compilation.** L'API du SDK prend une `ComponentActivity` et renvoie des types de coroutines (`StateFlow`, fonctions `suspend`), mais elle n'expose pas ces bibliothèques à votre code. Sans elles, les appels ne compilent pas, avec `Cannot access class 'androidx.activity.ComponentActivity'` ou `Cannot access class 'kotlinx.coroutines.flow.StateFlow'`. Une application qui dépend d'AppCompat (`androidx.appcompat:appcompat`) ou d'`androidx.activity:activity-compose` les a déjà toutes les deux ; sinon, ajoutez-les :

```kotlin
// app/build.gradle.kts — seulement si votre application n'a ni AppCompat ni activity-compose
dependencies {
    implementation("androidx.activity:activity-ktx:1.8.2")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
}
```

**Un partner ID et un config ID**, disponibles dans la [Console Sirdata](https://cmp.sirdata.io). Ils désignent la configuration éditeur que le SDK télécharge : textes, thème, finalités, partenaires, capping. Les exemples de cette page utilisent `1020` et `"hmDnl"` : remplacez-les par vos propres identifiants.

**Une licence pour l'usage en production.** Le SDK est un logiciel propriétaire, sous la [Sirdata CMP Mobile SDK Proprietary Licence v1.1](https://sirdata.com/legal/sdk-licence-v1.1.txt). L'utiliser dans une application distribuée à des utilisateurs finaux exige un abonnement valide et en cours à la Sirdata Consent Management Platform. Un abonnement pris par la Console Sirdata suffit : aucun accord signé n'est requis (sections 25.1 et 25.2). Sans abonnement, la licence est une licence d'évaluation de 90 jours : évaluation interne, développement, tests et intégration, sans distribution à des tiers ni sur un store d'applications (section 1.4). Abonnements : [sirdata.com](https://sirdata.com) ; questions sur la licence : <legal@sirdata.com>.

## 2. Ajouter le SDK au projet

Déclarez les deux dépôts dans `settings.gradle.kts`. Le SDK est sur Maven Central, et ses dépendances AndroidX et Compose ne sont publiées que sur le dépôt Maven de Google. Les projets créés par Android Studio déclarent déjà les deux.

```kotlin
// settings.gradle.kts
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}
```

Dans un projet plus ancien qui déclare ses dépôts dans le `build.gradle` racine, sous `allprojects { repositories { … } }`, ajoutez-les plutôt à cet endroit.

Ajoutez ensuite la dépendance au module de votre application.

DSL Kotlin :

```kotlin
// app/build.gradle.kts
dependencies {
    implementation("com.sirdata.cmp:android-sdk:2.0.0")
}
```

DSL Groovy :

```groovy
// app/build.gradle
dependencies {
    implementation 'com.sirdata.cmp:android-sdk:2.0.0'
}
```

Catalogue de versions :

```toml
# gradle/libs.versions.toml
[versions]
sirdataCmp = "2.0.0"

[libraries]
sirdata-cmp-android = { module = "com.sirdata.cmp:android-sdk", version.ref = "sirdataCmp" }
```

```kotlin
// app/build.gradle.kts
dependencies {
    implementation(libs.sirdata.cmp.android)
}
```

Vous ne déclarez que `android-sdk`. Gradle ajoute le reste :

* `com.sirdata.cmp:shared` et sa variante Android `com.sirdata.cmp:shared-android`, le cœur que partagent les SDK Android et iOS. Ne les déclarez jamais vous-même.
* Jetpack Compose UI et Material 3 (BOM Compose 2024.02.00), `androidx.activity` 1.8.2 et `androidx.lifecycle` 2.7.0, pour l'interface de consentement.
* Ktor 2.3.9 sur OkHttp, kotlinx.coroutines 1.8.0 et kotlinx.serialization 1.6.3, pour le réseau.
* AndroidSVG 1.4, pour dessiner les logos SVG qu'une configuration peut porter.

Si votre application utilise déjà une version plus récente de l'une d'elles, Gradle garde la plus récente.

Il n'y a rien à ajouter à votre `AndroidManifest.xml` pour le SDK lui-même : par la fusion des manifestes, il apporte les permissions `INTERNET` et `ACCESS_NETWORK_STATE` et déclare sa propre activité de consentement, non exportée. Il n'y a rien à ajouter pour R8 non plus (étape 9).

## 3. Initialiser le SDK

Créez une sous-classe d'`Application`, ou ouvrez celle que votre application possède déjà, et initialisez le SDK dans `onCreate()` :

```kotlin
package com.example.myapp

import android.app.Application
import com.sirdata.cmp.android.SirDataCMPApi

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        // Avant tout SDK publicitaire ou analytics.
        SirDataCMPApi.initialize(
            context = this,
            partnerId = 1020, // remplacez par votre partner ID
            configId = "hmDnl", // remplacez par votre config ID
        )
    }
}
```

Si la classe est nouvelle, déclarez-la sur l'élément `<application>` de `AndroidManifest.xml`. Gardez les attributs et les activités que votre manifeste a déjà :

```xml
<!-- app/src/main/AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:name=".MyApp">
        <!-- vos activités, inchangées -->
    </application>
</manifest>
```

Quatre règles :

* **Avant tout SDK publicitaire ou analytics.** Un SDK partenaire qui lit le stockage avant que la CMP ne se soit déclarée se comporte comme si aucune CMP n'était présente.
* **Sur le fil principal.** `Application.onCreate()` s'y exécute. Appelé depuis un autre fil, `initialize` journalise la violation sous le tag Logcat `SirDataCMP`, ou lève une exception quand la configuration active `debugMode`.
* **Une fois par processus.** Un second appel lève une `IllegalStateException` (« SirDataCMP is already initialized. Call shutdown() first. »). Un code qui peut s'exécuter deux fois peut d'abord tester `SirDataCMPApi.isInitialized()`.
* **Avant tout autre appel au SDK.** Avant `initialize`, les getters de chaînes et de booléens renvoient `""` ou `false`, `awaitConsentDisplayDecision()` répond aussitôt que rien ne doit s'afficher, et `getConsentState()`, `getConsentUpdates()`, `getGoogleConsentState()`, `acceptAll()` et les autres actions de consentement lèvent une `IllegalStateException`. Initialiser dans `Application.onCreate()` écarte tout cela.

Pendant l'appel, avant toute requête réseau, le SDK écrit `IABTCF_CmpSdkID` = `92` dans les `SharedPreferences` par défaut de l'application, avec `IABTCF_CmpSdkVersion` et `IABTCF_PolicyVersion` : un SDK partenaire qui lit le stockage à partir de là trouve une CMP. Tout le reste se fait ensuite, de façon asynchrone : le SDK restaure le consentement stocké, télécharge la configuration éditeur, les listes de partenaires et les textes, écrit `IABTCF_gdprApplies` et les autres clés qui n'attendent pas de choix (listées à l'étape 5), et émet l'événement `tcloaded` vers les écouteurs TCF in-app.

### Options

Pour plus de contrôle, construisez une `SirDataCMPConfig` :

```kotlin
package com.example.myapp

import android.app.Application
import com.sirdata.cmp.android.SirDataCMPApi
import com.sirdata.cmp.android.SirDataCMPConfig

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        val config = SirDataCMPConfig.Builder(partnerId = 1020, configId = "hmDnl")
            .gdprDefaultApplies(true) // hypothèse retenue tant que la configuration n'a pas répondu
            .debugMode(BuildConfig.DEBUG) // un mauvais usage du fil principal lève une exception, en debug seulement
            .build()
        SirDataCMPApi.initialize(context = this, config = config)
    }
}
```

| Option du builder                                                   | Par défaut                                                                                                                                                    | Effet                                                                                                                                                                                                            |
| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `gdprDefaultApplies(Boolean)`                                       | `true`                                                                                                                                                        | Si le RGPD s'applique tant que la configuration n'est pas encore chargée et que l'appareil ne donne aucun pays (SIM, réseau, puis locale). Une fois la configuration chargée, la réponse du serveur la remplace. |
| `debugMode(Boolean)`                                                | `false`                                                                                                                                                       | Un appel à `initialize`, `shutdown` ou `setCcpaOptOut` fait hors du fil principal lève une exception au lieu d'être journalisé. Rien d'autre ne change, la journalisation comprise.                              |
| `apiRoot(String)`, `cacheApiRoot(String)`, `consentApiRoot(String)` | `https://api.consentframework.com/api/v1/public`, `https://choices.consentframework.com/api/v1/public`, `https://mychoice.consentframework.com/api/v1/public` | Les trois hôtes auxquels parle le SDK. Ne les changez que pour passer par votre propre proxy.                                                                                                                    |
| `certificatePins(CertificatePins?)`                                 | aucun                                                                                                                                                         | Épinglage de certificats, par nom d'hôte : `CertificatePins(mapOf("api.consentframework.com" to listOf("sha256/…")))`. Une empreinte erronée bloque les requêtes du SDK.                                         |
| `language(String?)`, `theme(CmpTheme)`                              | —                                                                                                                                                             | Acceptées, mais **sans effet en 2.0.0** — voir ci-dessous.                                                                                                                                                       |

**`language(...)` et `theme(...)` n'ont aucun effet en 2.0.0.** Le builder les accepte et rien ne les lit : ne comptez pas sur elles pour imposer une langue ou un thème sombre. Ce qui décide réellement :

* **La langue de l'interface de consentement** est celle que fixe la configuration, quand elle en fixe une ; sinon la langue de l'appareil (`Locale.getDefault()`) ; sinon la langue par défaut de la configuration ; sinon l'anglais.
* **Son apparence** est le thème de la configuration, dans la Console Sirdata — couleurs, position, logo, taille du texte. Quand l'appareil est en mode sombre et que la configuration définit un thème sombre, c'est lui qui s'applique ; sinon le thème clair.

**`BuildConfig.DEBUG` exige d'activer `buildConfig`.** Depuis l'Android Gradle Plugin 8.0, la classe `BuildConfig` n'est générée que pour les modules qui la demandent : l'extrait ci-dessus ne compile donc pas dans un nouveau projet tant que vous n'avez pas ajouté :

```kotlin
// app/build.gradle.kts
android {
    buildFeatures {
        buildConfig = true
    }
}
```

`BuildConfig` est générée dans le `namespace` de votre module : l'extrait n'a pas besoin d'import parce que son package est ce namespace. Ailleurs, importez `<votre namespace>.BuildConfig`.

## 4. Présenter l'interface de consentement

Laissez le SDK décider s'il faut présenter l'interface de consentement, depuis votre activité de lancement :

```kotlin
package com.example.myapp

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
import com.sirdata.cmp.android.SirDataCMPApi
import kotlinx.coroutines.launch

// N'importe quelle ComponentActivity : si la vôtre étend AppCompatActivity ou FragmentActivity, gardez-la.
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // setContentView(...) ou setContent { ... }, comme d'habitude

        lifecycleScope.launch {
            val decision = SirDataCMPApi.awaitConsentDisplayDecision()
            if (decision.shouldDisplay) {
                SirDataCMPApi.showConsentBanner(this@MainActivity, decision.workflow)
            }
        }
    }
}
```

Ne testez **pas** la chaîne TC vous-même : un consentement partiel doit rouvrir la bannière, une fenêtre de capping active doit l'empêcher, et ni l'un ni l'autre ne se lit dans la chaîne.

`awaitConsentDisplayDecision()` attend la configuration éditeur, puis applique la même cascade que la CMP web : applicabilité du RGPD, consentement stocké, cookie wall, fenêtres de capping, et si le choix stocké couvre encore chaque finalité, partenaire et finalité éditeur actifs. Elle renvoie une `ConsentDisplayDecision` :

* `shouldDisplay` — s'il faut présenter l'interface de consentement ;
* `workflow` — comment : `MAIN` pour la bannière standard ; `APPLY_CHOICES` quand seuls des partenaires manquent au choix stocké, pour ne demander que ce qui reste à décider ; `COOKIEWALL` pour le cookie wall, ci-dessous. Passez-le tel quel à `showConsentBanner` ;
* `reason` — pourquoi, c'est-à-dire ce qu'il faut journaliser quand aucune bannière n'apparaît (voir [Logs](/applications-mobiles-sdk-natif/mobile-sdk/logging.md)).

`showConsentBanner` lance l'activité de consentement du SDK par-dessus la vôtre — la bannière, puis les écrans finalités et partenaires, ou l'écran US pour un utilisateur américain (étape 7) —, émet l'événement TCF `cmpuishown`, et rend la main aussitôt. L'activité de consentement se ferme d'elle-même quand l'utilisateur enregistre un choix ou la ferme, et votre activité reprend.

**Le cookie wall.** Quand la configuration l'active, un choix stocké qui n'accorde pas la finalité 1 (stocker des informations sur un appareil et/ou y accéder) fait revenir l'interface de consentement à chaque lancement, dans le workflow `COOKIEWALL`, quelles que soient les fenêtres de capping. C'est le fait de passer `decision.workflow` qui l'ouvre en cookie wall.

**Applications Jetpack Compose.** Gardez le même code dans le `onCreate()` de votre activité, à côté de `setContent { … }`. Depuis un composable, récupérez l'activité depuis `LocalContext`, comme à l'étape 6.

### Borner l'attente sur un écran de démarrage

L'attente sans limite dure autant que l'initialisation : sur un réseau mort, c'est la somme des timeouts HTTP. Bornez-la avec `timeoutMillis` :

```kotlin
// Fragment : remplace le bloc lifecycleScope de MainActivity.onCreate() ci-dessus
import androidx.lifecycle.lifecycleScope
import com.sirdata.cmp.android.SirDataCMPApi
import kotlinx.coroutines.launch

lifecycleScope.launch {
    val decision = SirDataCMPApi.awaitConsentDisplayDecision(timeoutMillis = 3_000)
    if (decision.shouldDisplay) {
        // Sans argument workflow : quand la configuration arrive après l'échéance,
        // l'interface de consentement adopte le workflow de la décision complète.
        SirDataCMPApi.showConsentBanner(this@MainActivity)
    }
}
```

Passé l'échéance, la décision est provisoire : elle sait seulement si le RGPD s'applique, selon l'estimation de l'appareil, et si un choix est stocké. Sa raison est `GDPR_NOT_APPLIES` quand cette estimation dit que le RGPD ne s'applique pas, et `CONFIG_NOT_LOADED` sinon. Un choix stocké mais incomplet n'est donc pas redemandé à ce lancement.

### Rouvrir depuis un écran de réglages

Voir l'étape 6 ci-dessous.

## 5. Lire le consentement dans votre code

```kotlin
// Fragment : dans n'importe quelle fonction, une fois le SDK initialisé
import com.sirdata.cmp.android.SirDataCMPApi

val tcString = SirDataCMPApi.getTcString() // "" tant qu'aucun choix n'est enregistré
val gdprApplies = SirDataCMPApi.isGdprApplies()
val deviceStorageOK = SirDataCMPApi.hasPurposeConsent(1) // finalité 1 : stocker/accéder sur un appareil
val googleOK = SirDataCMPApi.hasVendorConsent(755) // 755 : Google dans la Global Vendor List
```

* **Les getters lisent le choix enregistré**, dans le stockage, jamais les bascules que l'utilisateur est en train de manipuler à l'écran. `getTcString()`, `getAddtlConsent()`, `getGppString()`, `getUspString()`, `getPurposeConsents()` et `getVendorConsents()` renvoient `""`, et `hasPurposeConsent()` et `hasVendorConsent()` renvoient `false`, tant qu'aucun choix n'est enregistré.
* **Les identifiants de finalités et de partenaires** sont ceux de la Global Vendor List de l'IAB, à partir de 1.
* **`isGdprApplies()`** est la réponse du serveur une fois la configuration chargée — d'après le pays de l'adresse IP de l'appareil, sauf si la configuration impose le RGPD. Avant, c'est une estimation de l'appareil, avec `gdprDefaultApplies` quand l'appareil ne donne aucun pays.

**Où vivent les signaux.** Le SDK écrit chaque signal sous les clés IAB standard, dans le fichier `SharedPreferences` par défaut de l'application, `<applicationId>_preferences` — celui qu'ouvre `PreferenceManager.getDefaultSharedPreferences()`. Les SDK partenaires conformes les lisent d'eux-mêmes ; la liste complète est dans [Transmission du consentement aux SDK partenaires](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md#les-cles-maintenues-par-le-sdk). Les clés apparaissent dans cet ordre :

* pendant `initialize` : `IABTCF_CmpSdkID`, `IABTCF_CmpSdkVersion`, `IABTCF_PolicyVersion` ;
* une fois la configuration chargée, avant tout choix : `IABTCF_gdprApplies`, de nouveau `IABTCF_PolicyVersion` (tirée de la liste de partenaires chargée), `IABTCF_PublisherCC`, `IABTCF_PurposeOneTreatment`, `IABTCF_UseNonStandardTexts`, les chaînes de bits éditeur (`IABTCF_PublisherConsent`, `IABTCF_PublisherLegitimateInterests` et celles des finalités personnalisées) et, pour un utilisateur américain, les clés GPP avec la section US National ; un choix stocké est aussi réécrit pour correspondre à la configuration courante ;
* quand un choix est enregistré : la chaîne TC, les champs de bits finalités et partenaires, `IABTCF_AddtlConsent` et les clés GPP (`IABGPP_HDR_GppString`, `IABGPP_GppSID`) ;
* `IABUSPrivacy_String` seulement après un choix US (étape 7).

Les chaînes de bits éditeur sont donc présentes dès le premier lancement, avec les valeurs par défaut du SDK : leur présence ne signifie pas qu'un choix a été enregistré. `IABTCF_TCString`, si.

**Réagissez aux changements** plutôt que de lire une fois :

```kotlin
// Fragment : dans le onCreate() d'une activité
import androidx.lifecycle.lifecycleScope
import com.sirdata.cmp.android.SirDataCMPApi
import kotlinx.coroutines.launch

lifecycleScope.launch {
    SirDataCMPApi.getConsentUpdates().collect {
        // Lisez ici les valeurs enregistrées, et réévaluez ce que vous conditionnez au consentement.
        val deviceStorageOK = SirDataCMPApi.hasPurposeConsent(1)
    }
}
```

`getConsentUpdates()` est un compteur. Il émet sa valeur courante dès que vous le collectez, puis une nouvelle valeur à chaque changement des sélections du SDK : plusieurs pendant l'initialisation et pendant l'enregistrement d'un choix, et une par bascule que l'utilisateur actionne dans l'interface de consentement avant d'enregistrer. Lire les valeurs enregistrées dans le collecteur, comme ci-dessus, donne un résultat qui ne change qu'une fois un choix enregistré.

Un choix US — enregistré sur l'écran US, ou avec `setCcpaOptOut` (étape 7) — ne fait pas avancer le compteur. Relisez `getUspString()` et `getGppString()` à la fermeture de l'interface de consentement : le `onResume()` de votre activité s'exécute à ce moment-là.

**Les écouteurs TCF in-app ne distinguent pas les événements en 2.0.0.** Un écouteur enregistré avec `tcfApi().execute("addEventListener", 2, …)` est appelé sur `tcloaded`, `cmpuishown`, `useractioncomplete` et `cmpuiclosed`, mais la donnée qu'il reçoit ne porte pas d'`eventStatus`. Traitez chaque appel comme « quelque chose a peut-être changé » et lisez les valeurs enregistrées.

**Google Consent Mode.** Le SDK dérive les signaux de consentement Google du choix TCF enregistré :

```kotlin
// Fragment : une fois le SDK initialisé
import com.sirdata.cmp.android.SirDataCMPApi

val google = SirDataCMPApi.getGoogleConsentState()
val adStorage = google.adStorageGranted
val analyticsStorage = google.analyticsStorageGranted
val adUserData = google.adUserDataGranted
val adPersonalization = google.adPersonalizationGranted
```

C'est ce que transmettraient les adaptateurs de consentement ; `getLastForwardedConsentState()` est ce qu'ils ont transmis en dernier, `null` tant qu'aucun choix n'est enregistré dans la session. Comment chaque SDK partenaire — Google Mobile Ads, Firebase, AppsFlyer, Adjust, Branch, Meta — reçoit le consentement, et ce que vous avez à faire pour lui, est décrit dans [Transmission du consentement aux SDK partenaires](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md).

**Votre propre écran de consentement.** `acceptAll()`, `rejectAll()`, `saveConsent()` et `submitConsent(userAction)` enregistrent un choix sans l'interface du SDK. Ce sont des fonctions `suspend` qui renvoient un `ConsentResult` — `ConsentResult.Success` ou `ConsentResult.Error(message)` — et qui stockent le choix, émettent `useractioncomplete` et le transmettent aux adaptateurs de consentement, comme l'interface du SDK. `recordAskLater()` masque la bannière pendant une heure, une fenêtre fixe, et n'enregistre aucun choix.

## 6. Rouvrir depuis un écran de réglages

Les utilisateurs doivent pouvoir modifier ou retirer leur choix à tout moment. Donnez-leur un point d'entrée — une entrée « Paramètres de confidentialité » dans votre écran de réglages, par exemple — qui ouvre l'interface de consentement dans le workflow `MANUAL_DISPLAY` :

```kotlin
package com.example.myapp

import androidx.activity.ComponentActivity
import com.sirdata.cmp.android.SirDataCMPApi
import com.sirdata.cmp.model.Workflow

// N'importe quelle ComponentActivity : si la vôtre étend AppCompatActivity ou FragmentActivity, gardez-la.
class SettingsActivity : ComponentActivity() {
    // À appeler depuis le listener de clic de votre entrée « Paramètres de confidentialité ».
    fun openPrivacySettings() {
        SirDataCMPApi.showConsentBanner(this, Workflow.MANUAL_DISPLAY)
    }
}
```

Depuis un composable, récupérez l'activité depuis `LocalContext` :

```kotlin
package com.example.myapp

import android.content.Context
import android.content.ContextWrapper
import androidx.activity.ComponentActivity
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import com.sirdata.cmp.android.SirDataCMPApi
import com.sirdata.cmp.model.Workflow

@Composable
fun PrivacySettingsButton() {
    val activity = LocalContext.current.findComponentActivity()
    TextButton(onClick = { SirDataCMPApi.showConsentBanner(activity, Workflow.MANUAL_DISPLAY) }) {
        Text("Paramètres de confidentialité")
    }
}

tailrec fun Context.findComponentActivity(): ComponentActivity =
    when (this) {
        is ComponentActivity -> this
        is ContextWrapper -> baseContext.findComponentActivity()
        else -> error("No ComponentActivity in this context")
    }
```

Ce que `MANUAL_DISPLAY` change par rapport à la bannière affichée au lancement :

* la bannière affiche une croix de fermeture en haut, et un bouton « Tout refuser » quels que soient le bouton et le style de refus que fixe la configuration — même aucun —, pour que l'utilisateur puisse toujours retirer son consentement ;
* la croix ferme sans rien enregistrer, sauf avec la variante CNIL, où la fermeture enregistre le choix ;
* l'ouvrir relance la fenêtre de capping de la configuration, pendant laquelle la bannière ne s'affiche pas automatiquement ;
* elle est comptée dans vos statistiques Sirdata comme une réouverture (`load:revisit`, ou `ccpa_load:revisit` pour un utilisateur aux États-Unis).

Laissez ce point d'entrée accessible à tous les utilisateurs, y compris quand `isGdprApplies()` vaut `false` : pour un utilisateur américain, il ouvre l'écran US.

## 7. Utilisateurs américains

* **Les utilisateurs américains ne reçoivent jamais la bannière automatiquement.** La décision d'affichage suit le RGPD : pour un utilisateur que le serveur place hors de son périmètre, `awaitConsentDisplayDecision()` renvoie `shouldDisplay = false` avec la raison `GDPR_NOT_APPLIES`, et cela inclut les utilisateurs américains.
* **L'écran US s'ouvre quand votre application ouvre l'interface de consentement.** Quand le régime US s'applique, `showConsentBanner` affiche l'écran US à la place de la bannière TCF — intitulé « Your Privacy Choices » en anglais par défaut — avec un contrôle d'opposition qui couvre ce que propose la configuration (vente, partage, publicité ciblée, ou une combinaison) et un bouton « Save My Choices » ; quand la configuration ne propose aucune opposition, l'écran n'affiche ni l'un ni l'autre. Votre application a donc besoin d'un point d'entrée que les utilisateurs américains peuvent atteindre : celui des réglages de l'étape 6 en est un. Le libellé et l'emplacement qu'il vous faut relèvent de votre service juridique.
* **Enregistrer l'écran US enregistre le choix** par `setCcpaOptOut` : il écrit `IABUSPrivacy_String`, avec son indicateur d'opposition à `Y` ou `N`, et réencode la chaîne GPP (`IABGPP_HDR_GppString`, `IABGPP_GppSID`) avec ses sections US. Fermer l'écran sans enregistrer n'enregistre rien. Tant qu'aucun choix US n'est enregistré, `IABUSPrivacy_String` est absente et `getUspString()` renvoie `""`. Un choix US ne fait pas avancer le compteur de `getConsentUpdates()` (étape 5).
* **`isCcpaApplies()`** est la réponse du serveur, d'après le pays et l'État américain de l'utilisateur, une fois la configuration chargée ; avant, une estimation d'après le pays de l'appareil.
* **N'appelez vous-même `setCcpaOptOut(optedOut)` que depuis un contrôle que l'utilisateur actionne**, comme votre propre interrupteur d'opposition. Il enregistre que l'information a été donnée et la réponse de l'utilisateur : l'appeler de votre propre chef — `if (isCcpaApplies()) setCcpaOptOut(true)`, par exemple — enregistre une opposition que l'utilisateur n'a jamais exprimée. Appelez-le sur le fil principal ; `isCcpaOptedOut()` relit la réponse.

## 8. Tester et vérifier l'intégration

Utilisez un build de debug sur un appareil ou un émulateur, avec Logcat filtré sur les tags `SirDataCMP` et `SirDataCMPManager`.

* **L'applicabilité du RGPD est décidée par le serveur**, d'après le pays de l'adresse IP de l'appareil, sauf si la configuration impose le RGPD. En testant depuis un pays où le RGPD ne s'applique pas, vous n'obtenez pas de bannière, avec la raison `GDPR_NOT_APPLIES`.
* **Pour rejouer un premier lancement**, effacez les données de l'application (`adb shell pm clear <applicationId>`), ou appelez `SirDataCMPApi.clearConsent()` et redémarrez l'application. `clearConsent()` supprime le choix stocké et, jusqu'au lancement ou au choix suivant, `IABTCF_CmpSdkID` aussi.
* **Quand vous changez de partner ID ou de config ID, effacez aussi le consentement stocké.** Une chaîne TC appartient à la configuration éditeur qui l'a collectée.

Trois outils aident. Lisez les clés stockées d'un build de debug avec `run-as`, en remplaçant `com.example.myapp` par votre `applicationId`, suffixe compris :

```bash
adb shell run-as com.example.myapp cat shared_prefs/com.example.myapp_preferences.xml
```

Faites remonter une configuration qui n'a pas pu être chargée — la même raison que celle que Logcat affiche après `Config load failed:` sous `SirDataCMPManager` :

```kotlin
// Fragment : dans le onCreate() d'une activité
import android.util.Log
import androidx.lifecycle.lifecycleScope
import com.sirdata.cmp.android.SirDataCMPApi
import kotlinx.coroutines.launch

lifecycleScope.launch {
    SirDataCMPApi.getInitError().collect { error ->
        if (error != null) Log.w("CMP", "Configuration failed to load: $error")
    }
}
```

Appelez l'API TCF in-app :

```kotlin
// Fragment : n'importe où après initialize() ; le callback s'exécute sur le fil principal
import android.util.Log
import com.sirdata.cmp.android.SirDataCMPApi

SirDataCMPApi.tcfApi().execute("ping", 2, { data, _, success ->
    Log.d("CMP", "ping success=$success data=$data")
})
```

Puis vérifiez, dans cet ordre :

1. **Juste après le lancement**, `IABTCF_CmpSdkID` vaut `92` — avant que le réseau ait répondu.
2. **Logcat** affiche `Config loaded successfully` sous `SirDataCMPManager`. `Config load failed: …` signifie que la configuration n'a pas été chargée : vérifiez les identifiants et le réseau.
3. **`ping`** répond `success=true` et `cmpId=92`, avec `cmpStatus=stub` tant que la configuration n'est pas chargée, puis `cmpStatus=loaded`, `cmpLoaded=true` et `gdprApplies`. Le basculement peut arriver un instant après le retour d'`awaitConsentDisplayDecision()`.
4. **Au premier lancement**, la décision a `shouldDisplay = true` et la raison `CONSENT_REQUIRED`, et la bannière apparaît. [Logs](/applications-mobiles-sdk-natif/mobile-sdk/logging.md) montre comment journaliser la décision.
5. **Après « Tout accepter »**, le stockage contient `IABTCF_TCString`, `IABTCF_gdprApplies` = `1`, `IABTCF_PurposeConsents`, `IABTCF_VendorConsents` et `IABGPP_HDR_GppString`. `IABUSPrivacy_String` est absente : elle n'apparaît qu'après un choix US.
6. **Relancez l'application** (arrêt forcé, puis ouverture) : pas de bannière. La raison est `CONSENT_OK` après « Tout accepter », `CAPPING_ACTIVE` après « Tout refuser ».
7. **Votre entrée de réglages** rouvre la bannière avec la croix de fermeture et un bouton « Tout refuser ».
8. **Un build de release minifié** (étape 9) affiche aussi la bannière et enregistre un choix. Les builds de release ne sont pas débogables, donc `run-as` n'y fonctionne pas : vérifiez par les getters.
9. **Si votre application embarque Firebase Analytics, AppsFlyer, Adjust ou Branch**, `getRegisteredAdapters()` liste leurs adaptateurs et, après un choix, `getLastForwardedConsentState()` montre ce qui a été transmis.

## 9. Build de release

* **R8 et ProGuard : rien à ajouter.** Les artefacts du SDK portent leurs propres règles R8, y compris celles dont ses adaptateurs de consentement ont besoin. Les règles que vous aviez ajoutées pour les versions précédentes — `-keep class com.sirdata.cmp.** { *; }`, ou un `-dontwarn` pour slf4j — peuvent partir : le SDK livre lui-même le `-dontwarn`, et un `-keep` sur tout le SDK ne fait qu'empêcher R8 de le réduire.
* **`debugMode` dans les builds de debug uniquement**, par `.debugMode(BuildConfig.DEBUG)` comme à l'étape 3.
* **Vos propres partner ID et config ID**, pas les valeurs d'exemple.
* **Une licence de production** : un abonnement Sirdata CMP en cours, que fournit la Console Sirdata (étape 1).
* **Les mentions d'attribution.** Le SDK embarque des composants open source sous Apache License 2.0, et slf4j sous licence MIT. Une application qui l'embarque doit mettre à la disposition de ses utilisateurs le fichier `NOTICE-android` et le texte complet de ces deux licences, à un endroit qu'ils peuvent facilement atteindre — l'écran des licences open source de votre application, par exemple (section 26.1(b) de la licence). En 2.0.0, ces fichiers ne sont pas dans les artefacts Maven Central. Prenez-les dans le paquet npm `@sirdata/react-native-cmp@2.0.0` : `npm pack @sirdata/react-native-cmp@2.0.0` le télécharge sous forme d'archive `.tgz` qui contient `NOTICE-android` et, sous `THIRD-PARTY-LICENSES/`, `Apache-2.0.txt` et `MIT-slf4j.txt` (`BSL-1.0.txt` concerne iOS). Ou demandez-les à <legal@sirdata.com>.
* **Section « Sécurité des données » de Google Play.** Le SDK échange des données avec les serveurs de Sirdata : il télécharge la configuration, les listes de partenaires et les textes, et il envoie chaque choix enregistré — les chaînes de consentement — ainsi que des statistiques d'usage de l'interface de consentement (affichages, clics, réponses). Votre formulaire doit couvrir ce que le code tiers de votre application collecte ou partage ; voir la page de Google [Provide information for Google Play's Data safety section](https://support.google.com/googleplay/android-developer/answer/10787469).
* **Listes d'autorisation réseau, proxys et épinglage de certificats** doivent laisser passer trois hôtes : `api.consentframework.com`, `choices.consentframework.com` et `mychoice.consentframework.com`. Quand l'utilisateur ouvre le détail du stockage d'un partenaire dans l'écran des partenaires, le SDK récupère aussi la déclaration de stockage sur l'appareil de ce partenaire, à l'URL qu'il publie.

## 10. Monter de version depuis 1.2.0

1. **Passez la version** à `2.0.0` et recompilez. Quatre data classes publiques ont gagné un champ (`GoogleConsentState`, `UserAction`, `Settings`, `Info`) ; leurs constructeurs et signatures de `copy` précédents sont conservés en surcharges, si bien que le code source compile sans changement, mais une bibliothèque compilée contre 1.2.0 qui les appelle avec des arguments par défaut doit être recompilée.
2. **Retirez les règles R8** que vous aviez ajoutées pour le SDK (étape 9). 1.2.0 faisait échouer un build minifié sur `org.slf4j.impl.StaticLoggerBinder` ; 2.0.0 livre ses propres règles.
3. **Si votre code utilise les classes de l'interface de consentement du SDK** : `ConsentButtonStyle.CLOSE` est supprimé, et `ConsentUiState.scopeReminderOpensHostnames` est remplacé par `scopeReminderSegments` et `purposesText2Segments`, sans surcharge qui garde le constructeur précédent.
4. **AppsFlyer** : appelez `enableTCFDataCollection(true)` avant `start()` — voir [AppsFlyer](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md#appsflyer).
5. **Firebase Analytics** : l'adaptateur transmet désormais chaque choix, builds de release minifiés compris. Si votre application appelle elle-même le `setConsent` de Firebase pour les signaux que transmet l'adaptateur — pour contourner 1.2.0, par exemple —, retirez ces appels : voir [Firebase Analytics](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md#firebase-analytics).
6. **Attendez-vous à ce que l'entrée de réglages change d'aspect** : en `MANUAL_DISPLAY`, la bannière affiche désormais un bouton « Tout refuser » avec la croix de fermeture en haut (étape 6) ; 1.2.0 n'affichait aucun refus.
7. **Refaites les vérifications de l'étape 8.**

Tous les changements sont listés dans [Versions](/applications-mobiles-sdk-natif/mobile-sdk/versions.md).

## Pour continuer

* [Référence](/applications-mobiles-sdk-natif/mobile-sdk/reference.md) — chaque méthode et chaque événement
* [Transmission du consentement aux SDK partenaires](/applications-mobiles-sdk-natif/mobile-sdk/sharing-consent.md) — la partie qui fait fonctionner la publicité
* [Logs](/applications-mobiles-sdk-natif/mobile-sdk/logging.md) — ce que le SDK journalise, et la lecture de la décision d'affichage
* [Dépannage](/applications-mobiles-sdk-natif/mobile-sdk/troubleshooting.md) — symptômes et corrections
* [Versions](/applications-mobiles-sdk-natif/mobile-sdk/versions.md) — ce qui a changé à chaque version


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://cmp.docs.sirdata.net/applications-mobiles-sdk-natif/mobile-sdk/setup.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
