Zur Dokumentation springen

Android SDK

Das Kixo Android SDK unterstützt Kotlin 2.0+ und Java, setzt minSdk 24 (Android 7.0) voraus und ist gegen compileSdk 35 gebaut. Für dein eigenes targetSdk bleibt die Host-App selbst verantwortlich. Ein einzelner Aufruf von Kixo.configure in deiner Application.onCreate erfasst Screens, Taps, Sitzungen, Abstürze und Lifecycle-Events automatisch. Für Push-Tracking ist die unten beschriebene FCM-Bridge erforderlich. Automatisches Tracking von Netzwerkanfragen ist im aktuellen Android-Release nicht enthalten. Das SDK unterstützt außerdem Session Replay, Identität und Ziele.

Schnellstart

Drei Dateien: Maven-Repository hinzufügen, Abhängigkeit hinzufügen und dann zwei Zeilen in deine Application-Unterklasse einfügen.

Build-Voraussetzungen: compileSdk 35, minSdk 24, Kotlin 2.0+ oder Java sowie Java-17-Bytecode.

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

Hinweis

Damit ist die Analytics-Integration erledigt. Die Standard-Auto-Tracker sind standardmäßig aktiv; für Push brauchst du weiterhin die unten beschriebene FCM-Bridge. Einzelne Flags solltest du nur bei Bedarf mit KixoConfiguration.Builder(...) überschreiben.

In deine App einbinden

Das Kixo-Maven-Repository wird über GitHub Pages bereitgestellt. Füge es in settings.gradle.kts zusätzlich zu google() und mavenCentral() hinzu:

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

Deklariere die Abhängigkeit anschließend in deinem App-Modul:

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

Tipp

android.permission.INTERNET und android.permission.ACCESS_NETWORK_STATE sind bereits im SDK-Manifest enthalten. Die Benachrichtigungsberechtigungen bleiben Sache deiner App und werden erst deklariert, wenn du Push-Funktionen aktivierst.

Projekte mit mehreren Modulen

Die Gradle-Konfiguration implementation ist nicht transitiv: Wenn du implementation("io.kixo:kixo-android-sdk:0.1.20") in einem Bibliotheksmodul deklarierst, zum Beispiel :core_domain, wird Kixo für :app oder andere Consumer NICHT sichtbar. Es gibt zwei funktionierende Muster — wähle eines davon.

Muster A — jedes Modul, das Kixo aufruft, deklariert die Abhängigkeit selbst (empfohlen). hält den Classpath jedes Moduls schlank und vermeidet Kettenreaktionen bei Rebuilds. Verwende einen Versionskatalog (libs.kixo.sdk), damit du die Version nur an einer Stelle ändern musst.

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
}

Muster B — über api(...) re-exportieren. Nur eine Deklaration, aber damit tauchen Kixo-Typen im öffentlichen ABI des Bibliotheksmoduls auf — jede Versionsänderung erzwingt dann einen Rebuild aller abhängigen Module. Verwende das nur, wenn die Bibliothek Kixo-Typen in ihren eigenen öffentlichen Signaturen nutzt, also zum Beispiel KixoDiagnostics aus einer Funktion zurückgibt.

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
}

Warnung

Wenn in einem Modul beim Kompilieren Unresolved reference: Kixo auftaucht, fehlt diesem Modul die eigene Abhängigkeit auf das SDK. Füge die oben gezeigte implementation-Zeile hinzu oder verwende Muster B.

Initialisieren

Konfiguriere Kixo in deiner Application-Unterklasse — onCreate läuft vor jeder Activity, sodass jede Screen-Ansicht, jeder Tap und jedes Lifecycle-Event ab dem ersten Frame erfasst wird. Registriere Application mit android:name=".MyApp" in deinem Manifest.

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

Für feingranulare Einstellungen — etwa Auto-Tracking-Flags, Flush-Intervall, Replay-Sampling oder einen eigenen API-Host — erstellst du KixoConfiguration explizit:

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)

Hinweis

Idempotent. Ein zweiter Aufruf von configure im selben Prozess ist ein als WARN protokolliertes No-op — das SDK behält die erste Konfiguration bei. Events, die dein Auth-Singleton sendet, bevor bevor configure aufgerufen wird, werden gepuffert (maximal 50) und abgespielt, sobald das SDK initialisiert ist. Du kannst Kixo.identify(...) also schon in globalem Code aufrufen, bevor Application.onCreate abgeschlossen ist.

Events erfassen

Drei Grundbausteine tragen den Großteil deiner Instrumentierung: track für Events, markGoal für Conversion-Signale und addBreadcrumb für Kontext, der nicht an einzelne Events gebunden ist.

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

Tipp

Ziele werden abgestuft bewertet. Als Ziel markierte Events fließen in die Aktivierungs-Funnels von Kixo und in den täglichen Cronjob zur Änderungserkennung ein. Fällt das Volumen eines Ziels im Wochenvergleich um 70 %, erscheint es im Dashboard mit dem Badge Muss geprüft werden. Verwende markGoal für die wenigen wirklich wichtigen Momente, track für alles andere.

Standard-Events

Komfortschicht über Kixo.track für Events, die Kixo am Namen erkennt — also exakte String-Keys, die der Standard-Event-Detektor im Backend abgleicht. Mit Validierung der Property-Struktur zur Compile-Zeit und einer zentralen Stelle für die Benennung.

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)

Nutzer identifizieren

Ordne alle folgenden Events einer stabilen Nutzer-ID und einem Satz von Traits zu. Das Zusammenführen von anonymen und bekannten Nutzern übernimmt Kixo — Events, die vor identify erfasst wurden, werden nachträglich demselben Nutzer zugeordnet.

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

⚠️ Stolperfalle mit dem Dollarzeichen in Kotlin. Standard-Identity-Keys haben das Präfix $ ($email, $name, $first_name) — und in einem Kotlin-Stringliteral muss du das Dollarzeichen als "\$email" escapen. Wenn du "$email" schreibst, wird stattdessen deine Variable email interpoliert. Der Wert landet dann stillschweigend als Trait individuell und füllt die Audience-Spalten für E-Mail oder Name nie. Am einfachsten ist die typisierte Überladung (SDK 0.1.13+); damit ist dieser Fehler ausgeschlossen: Kixo.setUserProperty(StandardProperty.EMAIL, email).

Nutzer für die Segmentierung markieren

Verwende setUserProperty mit einem boolesch-Wert, um den Nutzer mit einem einfachen Ja/Nein-Merkmal zu versehen. Das Merkmal bleibt über App-Starts hinweg erhalten und kann für Segmente, E-Mail-Kampagnen und Chat-Abfragen genutzt werden — mehr als den SDK-Aufruf brauchst du nicht.

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

Properties werden über SharedPreferences appübergreifend gespeichert und automatisch an jedes ausgehende Event angehängt. Sag in Kixo Chat zum Beispiel "Sende eine Willkommens-E-Mail an Nutzer, bei denen subscribe true ist" — Kixo erstellt daraus das Segment und einen Vorlagenentwurf. Gelöscht werden sie bei Kixo.reset().

Standardkatalog für Properties

Reservierte Property-Schlüssel tragen das Präfix $, damit sie klar von deinen eigenen Traits getrennt sind. Der Katalog von Kixo umfasst 37 Schlüssel in 3 universellen Paketen (Identität, Geo, Lebenszyklus) und 5 B2B-Fachpaketen (Subscription, E-Commerce, Medien, Marktplatz, Loyalität). Setze einfach die Schlüssel, die zu deinem Produkt passen — das Dashboard passt sich an und zeigt nur die Pakete an, die du tatsächlich befüllst.

Identität

Immer relevant. Legt die Spalten im Profilkopf fest.

SchlüsselTypBeschreibung
$emailStringPrimäre E-Mail-Adresse, oft der Schlüssel zum Zusammenführen von Identitäten.
$phoneStringTelefonnummer im E.164-Format.
$nameStringVollständiger Anzeigename.
$first_nameStringVorname.
$last_nameStringNachname.
$avatar_urlStringVollständige URL zum Avatarbild des Nutzers.

Geo

Geografischer Kontext.

SchlüsselTypBeschreibung
$countryStringLändercode nach ISO 3166.
$cityStringStadtname.
$regionStringBundesland oder Provinz.
$timezoneStringIANA-Zeitzone wie America/Los_Angeles.
$languageStringIETF-Tag wie en oder ru-RU.
$localeStringVollständiger Locale-Identifier.

Lebenszyklus

Wann wir sie zuletzt gesehen haben.

SchlüsselTypBeschreibung
$createdISO8601Zeitpunkt der Registrierung oder Kontoerstellung.
$last_seenISO8601Zeitpunkt der letzten Interaktion.

Abonnement

Setzen, wenn dein Produkt Tarife hat.

SchlüsselTypBeschreibung
$planStringStufen-Slug — free, pro, enterprise.
$subscription_statusStringactive / trial / cancelled / past_due.
$trial_endsISO8601Wann die aktuelle Testphase endet.
$mrrZahlMonatlich wiederkehrender Umsatz in der Kontowährung.
$subscription_startedISO8601Beginn des aktuellen Abonnements.

E-Commerce

Setzen, wenn du Produkte verkaufst.

SchlüsselTypBeschreibung
$lifetime_ordersZahlAnzahl abgeschlossener Bestellungen.
$lifetime_revenueZahlGesamtausgaben.
$aovZahlDurchschnittlicher Bestellwert.
$last_purchaseISO8601Letzter erfolgreicher Kauf.
$first_purchaseISO8601Erster erfolgreicher Kauf.
$cart_abandoned_countZahlGesamtzahl der Warenkorbabbrüche.

Medien

Setzen, wenn du Inhalte veröffentlichst.

SchlüsselTypBeschreibung
$content_tierStringfree / premium / paid.
$subscribed_categoriesCSV-String oder ArrayKategorien, denen der Nutzer folgt.
$watch_time_totalZahlGesamte Wiedergabezeit in Sekunden.
$last_playedISO8601Zeitpunkt des letzten Wiedergabestarts.

Marktplatz

Setzen, wenn dein Produkt eine zweiseitige Plattform ist.

SchlüsselTypBeschreibung
$seller_tierStringSlug der Verkäuferstufe.
$buyer_tierStringTier-Slug auf Käuferseite.
$listings_countZahlAktive Einträge des Nutzers.
$reviews_countZahlBewertungen, die der Nutzer erhalten hat.
$verifiedbooleschKYC-Status.

Loyalität

Für Engagement- und Bonusprogramme setzen.

SchlüsselTypBeschreibung
$loyalty_pointsZahlAktuell einlösbarer Punktestand.
$vip_levelStringVIP-Stufen-Slug.
$referral_countZahlErfolgreiche Empfehlungen, die diesem Nutzer zugeschrieben werden.

Tipp

Dein Muster ist nicht dabei? Verwende für Custom Traits einfach ungeprefxte Keys. Sie erscheinen im Dashboard im Bereich „Custom Traits“, ohne die Profilspalten zu überladen. Die fünf Vertical Packs oben sind bewusst gewählte Annahmen für die häufigsten B2B-Modelle — kundenspezifische Begriffe (z. B. shipping_plan) bleiben ohne Präfix.

Super-Properties

Schlüssel/Wert-Paare pro Sitzung, die automatisch an jedes ausgehende Event angehängt werden. Anders als identify-Traits, die die Identität beschreiben, stehen Super-Properties für den Sitzungskontext — etwa aktive A/B-Variante, Build-Flavor oder aktivierte Feature-Flags. Sie bleiben über App-Starts hinweg erhalten und werden bei reset() gelöscht. Bei Kollisionen haben die properties auf track immer Vorrang.

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

Push-Benachrichtigungen

Es gibt zwei Integrationswege. Wähle A, wenn du FCM nutzt und die kürzeste funktionierende Lösung willst. Wähle B, wenn du bereits einen eigenen FirebaseMessagingService hast, den du nicht umbauen kannst, oder wenn du explizit steuern willst, welche FCM-Zustellungen Kixo sieht.

Option A — KixoFirebaseMessagingService erweitern (Auto-Tracking)

Erweitere KixoFirebaseMessagingService und rufe in deiner Überschreibung super.onMessageReceived(...) auf — Kixo sendet dann automatisch push_received (sichtbare Payload) oder push_silent (nur Daten). Die Basisklasse übernimmt außerdem die Registrierung von onNewToken, sofern du sie nicht überschreibst. Die Registrierung von AndroidManifest.xml bleibt unverändert gegenüber einem normalen FCM-Service.

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

Hinweis

Kixo kompiliert diese optionale Klasse gegen Firebase Messaging, bringt Firebase aber nicht transitiv in deine App. Das SDK deklariert Firebase als compileOnly; eine App, die diese Option nutzt, muss daher bereits von firebase-messaging abhängen — wie jede App mit einem FCM-Receiver.

Option B — die manuelle API aus deinem eigenen FCM-Service aufrufen

Registriere dein FCM-Token über FirebaseMessagingService.onNewToken bei Kixo und protokolliere dann jede Zustellung explizit. Nutze diesen Weg, wenn Kixo nur einen Teil deiner FCM-Zustellungen sehen soll. Auf Android wird Zustellungstracking derzeit nur für FCM unterstützt.

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 bietet keinen universellen Lifecycle-Hook für das Öffnen, Verwerfen oder die Aktionsschaltflächen von Benachrichtigungen. Leite diese Signale daher aus den Notification-Intents oder Receivern weiter, die deine App selbst anlegt:

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

Sitzungswiedergabe

Replay zeigt eine visuelle Rekonstruktion dessen, was der Nutzer gesehen hat. Bei jeder Erfassung kodiert das SDK ein komprimiertes Bildschirmbild (ein JPEG-Bild) zusammen mit einem strukturellen Schnappschuss der View-Hierarchie und lädt beides hoch — so kann der Player im Dashboard eine pixelgenaue Wiedergabe neben der Interaktionszeitleiste darstellen. Konfiguriere Replay für das Projekt in Dashboard → Einstellungen → Sitzungswiedergabe. Das SDK liest diese Projektrichtlinie automatisch ein und aktualisiert sie regelmäßig, einschließlich Maskierung, Erfassungsmodi und der Freigabe für Uploads über Mobilfunk.

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)

Wenn Uploads über Mobilfunk deaktiviert sind, wartet die Replay-Warteschlange auf ein zulässiges Netzwerk.

Tipp

Vor dem Hochladen maskieren. Kixo erfasst Pixel. Deshalb greift die Maskierung bevor, bevor irgendetwas das Gerät verlässt. Passwort- und E-Mail-Felder werden automatisch erkannt und geschwärzt, Text in strukturellen Schnappschüssen läuft durch einen PII-Filter, und jede View, die du mit setKixoMask(true) markierst, wird im Frame vor dem bevor des JPEG als deckendes Rechteck gerastert — ihre Pixel verlassen das Gerät nie. Screens in Jetpack Compose sind standardmäßig vollständig maskiert. Wenn du einen geprüften Screen einbeziehen willst, rufe setKixoMask(false) auf dem äußersten ComposeView auf. Im Dashboard prüfen Teammitglieder den Replay-Player zusammen mit der Ereigniszeitleiste.

Datenerfassung

Das SDK erfasst die in deinem Projekt aktivierten Daten sowie die Events und Properties, die deine App sendet.

Debugging

Kixo.diagnostics() liefert einen schreibgeschützten Snapshot des SDK-Zustands — praktisch für einen versteckten Debug-Screen oder einen Smoke-Test. Beantwortet „Warum kommen meine Events nicht an?“ ganz ohne 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

Erzwinge einen Flush aus deinem Test-Harness — blockiert bis zu timeoutMs für einen Netzwerk-Roundtrip:

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)

Compose Navigation

Activity- und Fragment-Routen erzeugen sofort screen_view-Events und liefern ohne weiteres Zutun strukturierte screen_visit-Einträge mit Metadaten zu Verweildauer und Ablauf. Bei Navigation mit Jetpack Compose rufst du Kixo.screen in einem LaunchedEffect auf, das an die Route gebunden ist — dann sieht das SDK genau ein Event pro Ziel, unabhängig von der Zahl der Recompositionen.

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

AI-Coding-Agenten

Die öffentliche Oberfläche des SDK ist klein und auf Code-Completion ausgelegt — jede Methode hängt am Singleton Kixo, jedes Kotlin-Beispiel in diesem Leitfaden beginnt mit import io.kixo.sdk.Kixo, und unser README enthält einen Block „AI agent quick reference“, den Tools wie Claude Code, Cursor und Codex direkt in ihren Kontext übernehmen können. Wenn dein Agent hängen bleibt, ist das der verlässliche Einstieg:

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

Hinweis

Alle Abschnitte oben sind auf diesen Ablauf zugeschnitten: Imports sind immer explizit, Typen immer ausgeschrieben, und das SDK-Singleton bekommt nie einen Alias. Gib diese Seite deinem Agenten und lass ihn damit arbeiten.