Vai alla documentazione

SDK Android

Il Kixo Android SDK supporta Kotlin 2.0+ e Java, richiede minSdk 24 (Android 7.0) ed è compilato contro compileSdk 35. La tua app host resta responsabile della propria targetSdk. Basta una chiamata a Kixo.configure nel tuo Application.onCreate per tracciare automaticamente schermate, tocchi, sessioni, crash ed eventi del ciclo di vita. Per le push serve il bridge FCM descritto sotto. Il tracciamento automatico delle richieste di rete non è incluso nell’attuale release Android. L’SDK supporta anche replay delle sessioni, identità e obiettivi.

Avvio rapido

Tre file: aggiungi il repository Maven, aggiungi la dipendenza, poi inserisci due righe nella tua sottoclasse di Application.

Requisiti di build: compileSdk 35, minSdk 24, Kotlin 2.0+ oppure Java, e 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")
        }
    }
}

Nota

L’integrazione analytics finisce qui. I tracciatori automatici standard sono attivi per default; per le push serve ancora il bridge FCM qui sotto. Sovrascrivi i singoli flag con KixoConfiguration.Builder(...) solo quando serve.

Aggiungi alla tua app

Il repository Maven di Kixo è ospitato su GitHub Pages. Aggiungilo insieme a google() e mavenCentral() in 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")
        }
    }
}

Poi dichiara la dipendenza nel modulo dell’app:

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

Suggerimento

android.permission.INTERNET e android.permission.ACCESS_NETWORK_STATE sono inclusi nel manifest dell’SDK. I permessi per le notifiche restano gestiti dalla tua app e vengono dichiarati quando abiliti le funzionalità push.

Progetti multi-modulo

La configurazione implementation di Gradle è non transitiva: dichiarare implementation("io.kixo:kixo-android-sdk:0.1.20") in un modulo libreria (ad esempio :core_domain) NON rende Kixo visibile a :app né ad altri consumer. Ci sono due approcci validi: scegline uno.

Pattern A — ogni modulo che chiama Kixo la dichiara direttamente (consigliato). mantiene minimo il classpath di ogni modulo ed evita ricompilazioni a catena. Usa un version catalog (libs.kixo.sdk) così aggiorni la versione in un solo punto.

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
}

Pattern B — riesporta tramite api(...). Una sola dichiarazione, ma l’ABI pubblica del modulo libreria includerà i tipi di Kixo: ogni aggiornamento di versione farà ricompilare tutti i moduli a valle. Usalo solo se la libreria riutilizza tipi di Kixo nelle proprie API pubbliche, per esempio quando una funzione restituisce 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
}

Attenzione

Se in compilazione compare Unresolved reference: Kixo in un modulo, quel modulo non dichiara la propria dipendenza dall’SDK: aggiungi la riga implementation qui sopra, oppure usa il Pattern B.

Inizializza

Configura Kixo nella tua sottoclasse di Application: onCreate viene eseguito prima di qualsiasi activity, quindi ogni schermata, tocco ed evento del ciclo di vita viene raccolto fin dal primo frame. Registra Application nel manifest con 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",
        )
    }
}

Per regolare con precisione i parametri — flag di tracciamento automatico, frequenza di flush, campionamento del replay, host API personalizzato — crea esplicitamente 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)

Nota

Idempotente. Una seconda chiamata a configure nello stesso processo viene ignorata e registrata come WARN: l’SDK mantiene la prima configurazione. Gli eventi accodati dal singleton di autenticazione in prima di prima che configure sia disponibile vengono mantenuti in buffer (massimo 50) e inviati quando l’SDK è pronto, quindi puoi chiamare Kixo.identify(...) da un globale prima che Application.onCreate completi l’inizializzazione.

Traccia gli eventi

La maggior parte della strumentazione passa da tre primitive: track per gli eventi, markGoal per i segnali di conversione e addBreadcrumb per il contesto non legato al singolo evento.

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",
)

Suggerimento

Gli obiettivi hanno livelli di priorità. Gli obiettivi contrassegnati alimentano i funnel di attivazione di Kixo e il job giornaliero di rilevamento delle variazioni: se il volume di un obiettivo cala del 70% rispetto alla settimana precedente, nella dashboard compare il badge Da rivedere. Usa markGoal per i pochi momenti che contano davvero; track per tutto il resto.

Eventi standard

Comodità sopra Kixo.track per gli eventi che Kixo riconosce dal nome: chiavi stringa letterali intercettate dal rilevatore di eventi standard del backend. Offre validazione a compile-time della struttura delle proprietà e un’unica fonte di verità per i nomi.

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)

Identifica gli utenti

Associa gli eventi successivi a un ID utente stabile e a un insieme di trait. Il collegamento tra utente anonimo e identificato avviene in Kixo: gli eventi raccolti prima di identify vengono attribuiti retroattivamente allo stesso utente.

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()

⚠️ Attenzione alla trappola del simbolo $ in Kotlin. Le chiavi di identità standard hanno il prefisso $ ($email, $name, $first_name) e, in una stringa Kotlin, deve fare l’escape del simbolo $ come "\$email". Se scrivi "$email", Kotlin interpola la variabile email: il valore finisce quindi silenziosamente in un trait personalizzato e non popola mai le colonne email / nome di Audience. La soluzione più semplice è usare l’overload tipizzato (SDK 0.1.13+), che evita l’errore alla radice: Kixo.setUserProperty(StandardProperty.EMAIL, email).

Assegna un tag a un utente per la segmentazione

Usa setUserProperty con un valore boolean per assegnare all’utente un semplice tag sì/no. Il tag persiste tra un avvio e l’altro e alimenta segmenti, campagne email e query in chat, senza altra configurazione oltre alla chiamata dell’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",
))

Le proprietà vengono persistite tramite SharedPreferences tra un avvio e l’altro e aggiunte automaticamente a ogni evento inviato. In chat puoi scrivere richieste come "invia un’email di benvenuto agli utenti per cui subscribe è true": Kixo costruisce il segmento e prepara il template per te. Vengono cancellate con Kixo.reset().

Catalogo standard delle proprietà

Le chiavi di proprietà riservate usano il prefisso $, così restano separate dai tuoi trait personalizzati. Il catalogo di Kixo comprende 37 chiavi distribuite in 3 pacchetti universali (identità, geolocalizzazione, ciclo di vita) e 5 pacchetti verticali B2B (abbonamenti, e-commerce, media, marketplace, fedeltà). Imposta solo quelle rilevanti per il tuo prodotto: la dashboard si adatta e mostra soltanto i pacchetti che popolari.

Identità

Sempre rilevante. Imposta le colonne dell'intestazione del profilo.

ChiaveTipoDescrizione
$emailstringaEmail principale, spesso usata come chiave di unione per la ricomposizione dell’identità.
$phonestringaNumero di telefono in formato E.164.
$namestringaNome visualizzato completo.
$first_namestringaNome.
$last_namestringaCognome.
$avatar_urlstringaURL completo dell'immagine avatar dell'utente.

Geo

Contesto geografico.

ChiaveTipoDescrizione
$countrystringaCodice paese ISO 3166.
$citystringaNome della città.
$regionstringaStato o provincia.
$timezonestringaZona IANA come America/Los_Angeles.
$languagestringaTag IETF come en o ru-RU.
$localestringaIdentificatore locale completo.

Ciclo di vita

Quando l’abbiamo visto.

ChiaveTipoDescrizione
$createdISO8601Data e ora di registrazione o creazione dell’account.
$last_seenISO8601Ora dell’ultima interazione.

Abbonamento

Impostalo se il tuo prodotto prevede piani.

ChiaveTipoDescrizione
$planstringaSlug del piano — free, pro, enterprise.
$subscription_statusstringaactive / trial / cancelled / past_due.
$trial_endsISO8601Data di scadenza del trial attuale.
$mrrnumeroRicavi ricorrenti mensili nella valuta dell’account.
$subscription_startedISO8601Data di inizio dell’abbonamento attuale.

E-commerce

Impostalo se vendi prodotti.

ChiaveTipoDescrizione
$lifetime_ordersnumeroNumero di ordini completati.
$lifetime_revenuenumeroSpesa totale.
$aovnumeroValore medio dell'ordine.
$last_purchaseISO8601Acquisto più recente concluso con successo.
$first_purchaseISO8601Primo acquisto completato con successo.
$cart_abandoned_countnumeroNumero totale di abbandoni del carrello.

Media

Impostalo se pubblichi contenuti.

ChiaveTipoDescrizione
$content_tierstringafree / premium / paid.
$subscribed_categoriesStringa CSV o arrayCategorie seguite dall'utente.
$watch_time_totalnumeroTempo di visione complessivo in secondi.
$last_playedISO8601Avvio della riproduzione più recente.

Marketplace

Impostalo se il tuo prodotto è una piattaforma a due versanti.

ChiaveTipoDescrizione
$seller_tierstringaSlug del piano lato venditore.
$buyer_tierstringaSlug del piano lato acquirente.
$listings_countnumeroAnnunci attivi dell'utente.
$reviews_countnumeroRecensioni ricevute dall’utente.
$verifiedbooleanStato KYC.

Fedeltà

Impostalo per programmi di coinvolgimento e ricompense.

ChiaveTipoDescrizione
$loyalty_pointsnumeroSaldo attuale dei punti riscattabili.
$vip_levelstringaSlug del piano VIP.
$referral_countnumeroSegnalazioni riuscite attribuite a questo utente.

Suggerimento

Non trovi il tuo schema? Usa chiavi semplici per gli attributi personalizzati. Compariranno nel pannello Custom Traits della dashboard senza occupare le colonne del profilo. I 5 pacchetti verticali qui sopra sono ipotesi ragionate sulle strutture B2B più comuni; la terminologia specifica del cliente, ad esempio shipping_plan, resta senza prefisso.

Super-property

Coppie chiave/valore di sessione, aggiunte automaticamente a ogni evento inviato. A differenza dei trait identify, che descrivono l’identità, le super-property descrivono il contesto della sessione: variante A/B attiva, flavor di build, feature flag abilitate. Restano persistenti tra un avvio e l’altro; vengono cancellate con reset(). In caso di conflitto, le properties del singolo evento passate a track hanno sempre la precedenza.

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()

Notifiche push

I percorsi di integrazione sono due. Scegli A se usi FCM e vuoi la configurazione funzionante più rapida; scegli B se hai già un FirebaseMessagingService personalizzato che non puoi riorganizzare oppure vuoi controllare in modo esplicito quali consegne FCM può vedere Kixo.

Opzione A — estendi KixoFirebaseMessagingService (tracciamento automatico)

Estendi KixoFirebaseMessagingService e chiama super.onMessageReceived(...) dal tuo override: Kixo emette automaticamente push_received (payload visibile) oppure push_silent (solo dati). La classe base gestisce anche la registrazione onNewToken, se non la sovrascrivi. La registrazione AndroidManifest.xml non cambia rispetto a un normale servizio FCM.

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
    }
}

Nota

Kixo compila questa classe opzionale contro Firebase Messaging, ma non aggiunge Firebase alla tua app come dipendenza transitiva. L’SDK dichiara Firebase come compileOnly; se scegli questa opzione, la tua app deve già dipendere da firebase-messaging, come accade per qualsiasi receiver FCM.

Opzione B — chiama l'API manualmente dal tuo servizio FCM

Registra il token FCM in Kixo tramite FirebaseMessagingService.onNewToken, poi registra esplicitamente ogni consegna. Usa questo percorso se vuoi che Kixo veda solo un sottoinsieme delle consegne FCM. Al momento, su Android il tracciamento delle consegne supporta solo 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 non espone un hook di ciclo di vita universale per apertura, chiusura o pulsanti d’azione delle notifiche. Inoltra quindi questi segnali dagli intent o dai receiver delle notifiche creati dalla tua app:

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 delle sessioni

Riproduci una ricostruzione visiva fedele di ciò che l’utente ha visto. A ogni acquisizione, l’SDK codifica un fotogramma compresso dello schermo (un’immagine JPEG) insieme a un’istantanea strutturale della gerarchia delle view e carica entrambi, così il player della dashboard può mostrare una riproduzione accurata al pixel accanto alla sequenza delle interazioni. Configura il replay del progetto in Dashboard → Impostazioni → Replay sessione. L’SDK legge e aggiorna automaticamente questa policy di progetto, incluse mascheratura, modalità di acquisizione e autorizzazione all’upload su rete cellulare.

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)

Quando l’upload su rete cellulare è disabilitato, i replay in coda aspettano una rete consentita.

Suggerimento

Maschera prima dell’upload. Kixo cattura i pixel, quindi il mascheramento avviene prima di che qualsiasi dato lasci il dispositivo. I campi password ed e-mail vengono rilevati e oscurati automaticamente; il testo nelle istantanee strutturali passa attraverso un filtro PII; e ogni view contrassegnata con setKixoMask(true) viene rasterizzata come rettangolo opaco nel fotogramma prima di della codifica JPEG: quei pixel non lasciano mai il dispositivo. Per impostazione predefinita, le schermate Jetpack Compose vengono mascherate integralmente (chiama setKixoMask(false) sul ComposeView più esterno per includere una schermata che hai già verificato). Nella dashboard, gli operatori consultano il player del replay accanto alla sequenza temporale degli eventi.

Raccolta dati

L’SDK raccoglie i dati abilitati nel progetto, oltre agli eventi e alle proprietà inviati dalla tua applicazione.

Debug

Kixo.diagnostics() restituisce un’istantanea in sola lettura dello stato dell’SDK: utile in una schermata di debug nascosta o in uno smoke test. Ti aiuta a capire perché gli eventi non stanno fluendo, senza aprire un debugger.

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

Forza un flush dal tuo harness di test: la chiamata può bloccare fino a timeoutMs per completare un round-trip di rete:

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)

Navigazione Compose

Le route di Activity / Fragment generano subito eventi screen_view e record strutturati screen_visit con metadati su permanenza e navigazione, senza configurazione aggiuntiva. Con Jetpack Compose Navigation, invia Kixo.screen da un LaunchedEffect indicizzato sulla route: così l’SDK registra un solo evento per destinazione, indipendentemente dal numero di ricomposizioni.

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()
        }
    }
}

Agenti di coding AI

La superficie pubblica dell’SDK è ridotta e pensata per l’autocompletamento: tutti i metodi vivono sul singleton Kixo, tutti gli esempi Kotlin di questa guida iniziano con import io.kixo.sdk.Kixo e il nostro README include un blocco "AI agent quick reference" che strumenti come Claude Code, Cursor e Codex possono incollare direttamente nel contesto. Se il tuo agente si blocca, questo è il punto di partenza canonico:

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."

Nota

Ogni sezione qui sopra è scritta pensando a questo flusso di lavoro: import sempre espliciti, tipi sempre dichiarati per nome e nessun alias per il singleton dell’SDK. Passa questa pagina al tuo agente e lascia che proceda.