Passer à la documentation

SDK Android

Le SDK Android de Kixo prend en charge Kotlin 2.0+ et Java, nécessite minSdk 24 (Android 7.0) et est compilé avec compileSdk 35. Votre application hôte reste responsable de son propre targetSdk. Un seul appel à Kixo.configure dans votre Application.onCreate active automatiquement le suivi des écrans, des appuis, des sessions, des crashs et des événements du cycle de vie. Le suivi des notifications push nécessite le pont FCM décrit ci-dessous. Le suivi automatique des requêtes réseau ne fait pas partie de la version Android actuelle. Le SDK prend aussi en charge le rejeu de session, l’identité et les objectifs.

Démarrage rapide

Trois fichiers. Ajoutez le dépôt Maven, ajoutez la dépendance, puis insérez deux lignes dans votre sous-classe de Application.

Prérequis de build : compileSdk 35, minSdk 24, Kotlin 2.0+ ou Java, et bytecode Java 17.

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo")
        }
    }
}

Remarque

L’intégration analytics s’arrête là. Les suivis automatiques standard sont activés par défaut ; pour les notifications push, il faut encore ajouter le pont FCM ci-dessous. Ne redéfinissez les options individuelles avec KixoConfiguration.Builder(...) qu’en cas de besoin.

Ajouter à votre application

Le dépôt Maven de Kixo est hébergé sur GitHub Pages. Ajoutez-le aux côtés de google() et mavenCentral() dans settings.gradle.kts :

kotlin
// settings.gradle.kts
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo")
        }
    }
}

Déclarez ensuite la dépendance dans le module de votre application :

kotlin
// app/build.gradle.kts
dependencies {
    implementation("io.kixo:kixo-android-sdk:0.1.20")
}

Conseil

android.permission.INTERNET et android.permission.ACCESS_NETWORK_STATE sont inclus dans le manifeste du SDK. Les autorisations de notification restent gérées par votre application et ne sont déclarées que si vous activez les fonctionnalités push.

Projets multi-modules

La configuration implementation de Gradle est non transitive : déclarer implementation("io.kixo:kixo-android-sdk:0.1.20") dans un module de bibliothèque (par exemple :core_domain) ne rend PAS Kixo visible pour :app ni pour aucun autre consommateur. Deux approches fonctionnent : choisissez-en une.

Modèle A — chaque module qui appelle Kixo le déclare lui-même (recommandé). réduit au minimum le classpath de chaque module et évite les recompilations en cascade. Utilisez un catalogue de versions (libs.kixo.sdk) pour ne modifier la version qu’à un seul endroit.

kotlin
// :core_domain/build.gradle.kts
dependencies {
    implementation("io.kixo:kixo-android-sdk:0.1.20")   // local use only
}

// :app/build.gradle.kts
dependencies {
    implementation(project(":core_domain"))
    implementation("io.kixo:kixo-android-sdk:0.1.20")   // declared again — fine
}

Modèle B — réexporter via api(...). Une seule déclaration suffit, mais l’ABI publique du module de bibliothèque inclut alors des types Kixo : à chaque changement de version, tous les modules en aval doivent être recompilés. À n’utiliser que si la bibliothèque réemploie des types Kixo dans ses signatures publiques, par exemple si une fonction renvoie KixoDiagnostics.

kotlin
// :core_domain/build.gradle.kts
dependencies {
    api("io.kixo:kixo-android-sdk:0.1.20")              // re-exposed
}

// :app/build.gradle.kts
dependencies {
    implementation(project(":core_domain"))            // gets Kixo for free
}

Avertissement

Si Unresolved reference: Kixo apparaît à la compilation dans un module, c’est que ce module n’a pas sa propre dépendance au SDK. Ajoutez la ligne implementation ci-dessus, ou utilisez le modèle B.

Initialiser

Configurez Kixo depuis votre sous-classe de Application : onCreate s’exécute avant toute activité, ce qui permet de capturer chaque affichage d’écran, appui et événement du cycle de vie dès la première image. Déclarez le Application dans votre manifeste avec android:name=".MyApp".

kotlin
import android.app.Application
import io.kixo.sdk.Kixo

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        Kixo.configure(
            context   = this,
            projectId = "kx_proj_YOUR_PROJECT_ID",
            apiKey    = "kx_key_YOUR_API_KEY",
        )
    }
}

Pour les réglages fins (options de suivi automatique, cadence d’envoi, échantillonnage du rejeu, hôte API personnalisé), construisez explicitement un KixoConfiguration :

kotlin
import io.kixo.sdk.Kixo
import io.kixo.sdk.KixoConfiguration

val config = KixoConfiguration.Builder(
    projectId = "kx_proj_YOUR_PROJECT_ID",
    apiKey    = "kx_key_YOUR_API_KEY",
)
    .autoTrackScreens(true)
    .autoTrackTaps(true)
    .autoTrackNetwork(false)  // reserved; no-op in the current Android release
    .autoTrackCrashes(true)
    .autoTrackSessions(true)
    .autoTrackPush(true)
    .flushIntervalMillis(30_000)
    .flushAt(20)
    .maxBufferSize(200)
    .build(applicationContext)

Kixo.configure(this, config)

Remarque

Idempotent. Un deuxième appel à configure dans le même processus ne fait rien et génère un log WARN : le SDK conserve la première configuration. Les événements mis en file par votre singleton d’authentification avant que avant configure soit disponible sont mis en tampon (50 maximum), puis rejoués une fois le SDK initialisé. Vous pouvez donc appeler Kixo.identify(...) depuis un global avant la fin de Application.onCreate.

Suivre des événements

L’essentiel de votre instrumentation repose sur trois primitives : track pour les événements, markGoal pour les signaux de conversion et addBreadcrumb pour le contexte non événementiel.

kotlin
import io.kixo.sdk.Kixo

Kixo.track("video_played", mapOf(
    "video_id"    to "vid_42",
    "duration_ms" to 18_500,
    "autoplay"    to false,
))

// markGoal(name, value?, currency?, properties?) — pass extra context
// through the named 'properties' argument (a Map can't be the 2nd
// positional arg; that slot is the Double 'value').
Kixo.markGoal("activated", properties = mapOf(
    "step" to "onboarding_completed",
))

// Revenue goals use the typed value + currency parameters:
Kixo.markGoal("purchase_completed", value = 49.99, currency = "USD")

Kixo.addBreadcrumb(
    message  = "user toggled dark mode",
    category = "ui",
    level    = "info",
)

Conseil

Les objectifs sont notés par niveau d’importance. Les objectifs marqués alimentent les tunnels d’activation de Kixo et la tâche cron quotidienne de détection des variations : si le volume d’un objectif baisse de 70 % d’une semaine sur l’autre, il apparaît dans votre tableau de bord avec le badge À relire. Réservez markGoal aux quelques moments qui comptent vraiment ; utilisez track pour le reste.

Événements standard

Surcouche de Kixo.track pour les événements que Kixo reconnaît par leur nom : des clés textuelles exactes que le détecteur d’événements standard du backend sait identifier. Validation à la compilation de la forme des propriétés, avec une source unique de vérité pour le nommage.

kotlin
import io.kixo.sdk.Kixo
import io.kixo.sdk.SubscriptionInterval

Kixo.trackPurchase(
    amount    = 49.99,
    currency  = "USD",
    productId = "pro_yearly",
)

Kixo.trackSubscriptionStart(
    plan     = "pro",
    amount   = 9.99,
    currency = "USD",
    interval = SubscriptionInterval.MONTH,
)

Kixo.trackSignup(method = "google")
Kixo.trackTrialStart(plan = "pro", days = 14)
Kixo.trackCancel(plan = "pro", reason = "too_expensive")
Kixo.trackUpgrade(fromPlan = "free", toPlan = "pro")
Kixo.trackActivation(event = "first_post_published")
Kixo.trackShare(channel = "twitter", contentId = "post_123")
Kixo.trackInvite(channel = "email", recipientCount = 5)

Identifier les utilisateurs

Associe les événements suivants à un identifiant utilisateur stable et à un ensemble de traits. Le rapprochement entre anonyme et utilisateur connu se fait dans Kixo : les événements capturés avant identify sont réattribués rétroactivement au même utilisateur.

kotlin
import io.kixo.sdk.Kixo

// Reserved standard property keys carry a $-prefix (Mixpanel
// convention) so they namespace away from your own custom traits
// and promote to the dashboard's profile columns. See
// io.kixo.sdk.StandardProperty for the typed catalogue, or the
// "Standard property catalog" section below for the full 37-key list.
Kixo.identify("user_123", mapOf(
    "$email" to "jane@example.com",     // identity
    "$name"  to "Jane Doe",              // identity
    "$plan"  to "pro",                   // subscription pack
    "$lifetime_orders" to 12,            // e-commerce pack
    "signup_source" to "twitter_ad",     // custom trait
))

// Logout: clear identity, super-properties, and the persisted queue.
Kixo.reset()

⚠️ Piège du signe dollar en Kotlin. Les clés d’identité standard sont préfixées par $ ($email, $name, $first_name) — et dans un littéral Kotlin, vous doit échapper le signe dollar en "\$email". Si vous écrivez "$email", Kotlin interpole votre variable email : la valeur est alors enregistrée discrètement comme trait personnalisé et n’alimente jamais les colonnes e-mail / nom dans Audience. Le plus simple est d’utiliser la surcharge typée (SDK 0.1.13+), impossible à rater : Kixo.setUserProperty(StandardProperty.EMAIL, email).

Ajouter un tag de segmentation à un utilisateur

Utilisez setUserProperty avec une valeur booléen pour ajouter à l’utilisateur un indicateur simple oui/non. Cet indicateur persiste entre les lancements et sert aux segments, aux campagnes e-mail et aux requêtes dans le chat, sans configuration supplémentaire au-delà de l’appel au SDK.

kotlin
// Tag a user as subscribed — segments + campaigns can target this
Kixo.setUserProperty("subscribe", true)

// VIP membership
Kixo.setUserProperty("vip", true)

// String + numeric values work too
Kixo.setUserProperty("plan_tier", "enterprise")
Kixo.setUserProperty("lifetime_orders", 42)

// Bulk-set
Kixo.setUserProperties(mapOf(
    "subscribe" to true,
    "plan_tier" to "enterprise",
))

Les propriétés persistent via SharedPreferences d’un lancement à l’autre et sont ajoutées automatiquement à chaque événement sortant. Dans le chat, dites par exemple "envoyer un e-mail de bienvenue aux utilisateurs pour lesquels subscribe est true" — Kixo crée le segment et prépare le modèle pour vous. Elles sont effacées lors de Kixo.reset().

Catalogue des propriétés standard

Les clés de propriété réservées portent le préfixe $, ce qui les isole de vos attributs personnalisés. Le catalogue de Kixo couvre 37 clés réparties en 3 packs universels (identité, géolocalisation, cycle de vie) et 5 packs métier B2B (abonnement, e-commerce, médias, marketplace, fidélité). Définissez uniquement celles qui s’appliquent à votre produit : le dashboard s’adapte et n’affiche que les packs que vous renseignez.

Identité

Toujours pertinent. Définit les colonnes d’en-tête du profil.

CléTypeDescription
$emailchaîne de caractèresAdresse e-mail principale, souvent utilisée comme clé de rapprochement d’identité.
$phonechaîne de caractèresNuméro de téléphone au format E.164.
$namechaîne de caractèresNom d’affichage complet.
$first_namechaîne de caractèresPrénom.
$last_namechaîne de caractèresNom de famille.
$avatar_urlchaîne de caractèresURL complète de l’image d’avatar de l’utilisateur.

Géographie

Contexte géographique.

CléTypeDescription
$countrychaîne de caractèresCode pays ISO 3166.
$citychaîne de caractèresNom de la ville.
$regionchaîne de caractèresÉtat ou province.
$timezonechaîne de caractèresZone IANA telle que America/Los_Angeles.
$languagechaîne de caractèresTag IETF tel que en ou ru-RU.
$localechaîne de caractèresIdentifiant de langue complet.

Cycle de vie

À quel moment les avons-nous vus ?

CléTypeDescription
$createdISO8601Date d’inscription ou de création du compte.
$last_seenISO8601Date du dernier engagement.

Abonnement

À renseigner si votre produit propose des offres.

CléTypeDescription
$planchaîne de caractèresSlug du palier — free, pro, enterprise.
$subscription_statuschaîne de caractèresactive / trial / cancelled / past_due.
$trial_endsISO8601Date d’expiration de l’essai en cours.
$mrrnombreRevenu mensuel récurrent dans la devise du compte.
$subscription_startedISO8601Date de début de l’abonnement en cours.

E-commerce

À renseigner si vous vendez des produits.

CléTypeDescription
$lifetime_ordersnombreNombre de commandes finalisées.
$lifetime_revenuenombreDépenses totales.
$aovnombreValeur moyenne des commandes.
$last_purchaseISO8601Dernier achat réussi.
$first_purchaseISO8601Premier achat réussi.
$cart_abandoned_countnombreNombre total d’abandons de panier.

Médias

À renseigner si vous publiez du contenu.

CléTypeDescription
$content_tierchaîne de caractèresfree / premium / paid.
$subscribed_categoriesChaîne CSV ou tableauCatégories suivies par l’utilisateur.
$watch_time_totalnombreTemps de visionnage cumulé en secondes.
$last_playedISO8601Dernier démarrage de lecture.

Marketplace

À renseigner si votre produit est une plateforme à deux versants.

CléTypeDescription
$seller_tierchaîne de caractèresSlug du palier côté vendeur.
$buyer_tierchaîne de caractèresSlug du niveau côté acheteur.
$listings_countnombreAnnonces actives appartenant à l’utilisateur.
$reviews_countnombreAvis reçus par l’utilisateur.
$verifiedbooléenStatut KYC.

Fidélité

À renseigner pour les programmes d’engagement et de récompenses.

CléTypeDescription
$loyalty_pointsnombreSolde actuel des points échangeables.
$vip_levelchaîne de caractèresSlug du palier VIP.
$referral_countnombreParrainages réussis attribués à cet utilisateur.

Conseil

Vous ne trouvez pas votre cas ? Utilisez des clés simples pour les attributs personnalisés. Elles apparaissent dans le panneau Custom Traits du dashboard sans encombrer les colonnes de profil. Les 5 ensembles sectoriels ci-dessus reflètent les structures B2B les plus courantes — la terminologie propre à chaque client (par ex. shipping_plan) doit rester en clé simple.

Super-propriétés

Paires clé/valeur de session ajoutées automatiquement à chaque événement sortant. Contrairement aux traits identify, qui décrivent l’identité, les super-propriétés décrivent le contexte de session : variante A/B active, flavor de build, feature flags activés. Elles persistent entre les lancements et sont effacées lors de reset(). En cas de conflit, les properties définies sur track pour un événement donné l’emportent toujours.

kotlin
import io.kixo.sdk.Kixo

Kixo.setSuperProperty("build_flavor", "beta")
Kixo.setSuperProperties(mapOf(
    "ab_variant"        to "B",
    "referrer_campaign" to "autumn-launch",
))

// Sugar for A/B tracking — stored as 'experiment_<id>'.
Kixo.setExperimentVariant("checkout_v2", "variant_a")

Kixo.unsetSuperProperty("build_flavor")
Kixo.clearSuperProperties()

Notifications push

Deux voies d’intégration. Choisissez A si vous utilisez FCM et voulez la configuration opérationnelle la plus courte ; choisissez B si vous avez déjà un FirebaseMessagingService personnalisé que vous ne pouvez pas réorganiser, ou si vous voulez contrôler explicitement quelles livraisons FCM sont visibles par Kixo.

Option A — étendre KixoFirebaseMessagingService (suivi automatique)

Créez une sous-classe de KixoFirebaseMessagingService et appelez super.onMessageReceived(...) dans votre surcharge : Kixo émet automatiquement push_received (payload visible) ou push_silent (données seules). La classe de base gère aussi l’enregistrement onNewToken si vous ne le surchargez pas. L’enregistrement AndroidManifest.xml ne change pas par rapport à un service FCM classique.

kotlin
import com.google.firebase.messaging.RemoteMessage
import io.kixo.sdk.KixoFirebaseMessagingService

class MyMessagingService : KixoFirebaseMessagingService() {
    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)  // Kixo auto-tracks push_received
        // … your own routing / notification display
    }
}

Remarque

Kixo compile cette classe optionnelle avec Firebase Messaging, mais n’ajoute pas Firebase transitivement à votre application. Le SDK déclare Firebase en compileOnly ; si vous choisissez cette option, votre application doit déjà dépendre de firebase-messaging, comme pour tout receiver FCM.

Option B — appeler l’API manuellement depuis votre propre service FCM

Enregistrez votre jeton FCM auprès de Kixo via FirebaseMessagingService.onNewToken, puis journalisez explicitement chaque livraison. Choisissez cette voie si vous voulez que Kixo ne voie qu’une partie des livraisons FCM. Sur Android, le suivi des livraisons ne prend actuellement en charge que FCM.

kotlin
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import io.kixo.sdk.Kixo
import io.kixo.sdk.PushProvider

class MyMessagingService : FirebaseMessagingService() {
    override fun onNewToken(token: String) {
        Kixo.setPushToken(token, PushProvider.FCM)
    }

    override fun onMessageReceived(message: RemoteMessage) {
        // Convert the FCM payload to a Map<String, Any?> and log it —
        // Kixo correlates this with the open / dismiss it sees later.
        Kixo.logPushReceived(message.data.toMap(), appState = "background")
    }
}

Android n’expose pas de point d’entrée universel du cycle de vie pour l’ouverture, la fermeture ou les boutons d’action des notifications. Faites donc remonter ces signaux depuis les intents ou receivers de notification créés par votre application :

kotlin
import io.kixo.sdk.Kixo

Kixo.logPushOpened(payload = pushPayload)            // open
Kixo.logPushOpened(payload = pushPayload, actionId = "reply")  // action-button tap
Kixo.logPushDismissed(payload = pushPayload)         // swipe-away

Replay de session

Rejouez une reconstruction visuelle fidèle de ce que l’utilisateur a vu. À chaque capture, le SDK encode une image compressée de l’écran (JPEG) ainsi qu’un instantané structurel de la hiérarchie de vues, puis envoie les deux ; le lecteur du tableau de bord peut ainsi restituer une lecture fidèle au pixel près, avec la chronologie des interactions à côté. Configurez le rejeu au niveau du projet dans Dashboard → Settings → Relecture de session. Le SDK lit et actualise automatiquement cette politique de projet, y compris le masquage, les modes de capture et l’autorisation d’envoi sur réseau cellulaire.

kotlin
import io.kixo.sdk.Kixo
import io.kixo.sdk.KixoConfiguration

val config = KixoConfiguration.Builder(
    projectId = "kx_proj_YOUR_PROJECT_ID",
    apiKey    = "kx_key_YOUR_API_KEY",
)
    .build(applicationContext)

Kixo.configure(this, config)

Quand l’envoi sur réseau cellulaire est désactivé, les rejeux en attente patientent jusqu’à ce qu’un réseau autorisé soit disponible.

Conseil

Masquer avant l’envoi. Kixo capture des pixels : le masquage s’exécute donc avant toute sortie de données de l’appareil. Les champs mot de passe et e-mail sont détectés puis masqués automatiquement ; le texte des instantanés structurels passe par un filtre PII ; et toute vue marquée avec setKixoMask(true) est rasterisée en rectangle opaque dans l’image avant l’encodage JPEG — ses pixels ne quittent jamais l’appareil. Les écrans Jetpack Compose sont entièrement masqués par défaut ; appelez setKixoMask(false) sur le ComposeView le plus externe pour inclure un écran que vous avez vérifié. Dans le tableau de bord, les opérateurs nettoient aussi le lecteur de rejeu et sa chronologie d’événements.

Collecte de données

Le SDK capture les données activées dans votre projet, ainsi que les événements et propriétés envoyés par votre application.

Débogage

Kixo.diagnostics() renvoie un instantané en lecture seule de l’état du SDK — pratique dans un écran de débogage caché ou un smoke test. De quoi répondre à « pourquoi mes événements ne remontent pas ? » sans débogueur.

kotlin
import io.kixo.sdk.Kixo

val diag = Kixo.diagnostics()
Log.d("Kixo", "queued=${diag.queue.bufferedEventCount}")
Log.d("Kixo", "paused=${diag.paused}")                  // collection paused state
Log.d("Kixo", "lifecycleState=${diag.lifecycleState}")  // SDK lifecycle state

Forcez un envoi depuis votre harness de test — l’appel peut bloquer jusqu’à timeoutMs, le temps d’un aller-retour réseau :

kotlin
import io.kixo.sdk.Kixo

// Async fire-and-forget — returns immediately.
Kixo.flush()

// Blocking variant for instrumentation tests. Never call on the main thread.
val landed: Boolean = Kixo.flushBlocking(timeoutMs = 5_000L)
assertTrue(landed)

Navigation Jetpack Compose

Les routes Activity / Fragment produisent immédiatement des événements screen_view et des enregistrements screen_visit structurés, avec les métadonnées de durée d’affichage et de navigation, sans configuration supplémentaire. Avec Jetpack Compose Navigation, appelez Kixo.screen depuis un LaunchedEffect indexé sur la route : le SDK ne verra alors qu’un événement par destination, quel que soit le nombre de recompositions.

kotlin
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import io.kixo.sdk.Kixo

@Composable
fun AppNavHost(nav: NavHostController) {
    NavHost(navController = nav, startDestination = "home") {
        composable("home") {
            LaunchedEffect("home") { Kixo.screen("HomeScreen") }
            HomeScreen()
        }
        composable("settings") {
            LaunchedEffect("settings") { Kixo.screen("SettingsScreen") }
            SettingsScreen()
        }
    }
}

Agents de développement AI

La surface publique du SDK est réduite et pensée pour l’autocomplétion : toutes les méthodes sont sur le singleton Kixo, tous les exemples Kotlin de ce guide commencent par import io.kixo.sdk.Kixo, et notre README inclut un bloc « AI agent quick reference » que des outils comme Claude Code, Cursor et Codex peuvent coller directement dans leur contexte. Si votre agent se bloque, partez de ceci :

kotlin
// Tell your AI coding agent:
// "Integrate the Kixo Android SDK using io.kixo:kixo-android-sdk
//  from https://raw.githubusercontent.com/kixoio/kixo-android-sdk/main/repo.
//  Call Kixo.configure(this, projectId, apiKey) in Application.onCreate.
//  Then use Kixo.track / Kixo.identify / Kixo.markGoal as needed."

Remarque

Chaque section ci-dessus a été écrite pour ce mode de travail : imports toujours explicites, types toujours nommés, et singleton du SDK jamais aliasé. Donnez cette page à votre agent et laissez-le faire.