Respectlytics Respect lytics
Menu
Kotlin (Android) Level complete Privacy-first

How to track level-complete events in Kotlin (Android) games without personal data

Level complete is the central progression event in mobile games and the place game analytics SDKs default to building per-player skill graphs (level number, time-to-complete, attempts, score, items used). Respectlytics helps developers avoid collecting personal data in the first place: in Kotlin (Android), level complete is one named event per level, with the level encoded into the event name. Below: how to keep your level taxonomy bounded, why time-to-complete becomes a server-side derivation, and what stays in your game backend.

Fire on level-completion success — after the win-state animation begins, before any post-level dialog. Encode the level into the event name (level_complete_1, level_complete_2). Don't pass score, attempts, or duration.

Install the Kotlin (Android) SDK

kotlin Respectlytics
// build.gradle.kts (app module)
dependencies {
    implementation("com.respectlytics:respectlytics-kotlin:3.0.0")
}

Pure Kotlin coroutines implementation. No Java dependencies, no Google Play Services dependencies. ~300KB DEX overhead — compare to roughly 3.8MB for Firebase Analytics (a measurable cold-start improvement on lower-end devices).

Initialize Respectlytics in Kotlin (Android)

kotlin Respectlytics
import com.respectlytics.android.Respectlytics

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Respectlytics.configure(this, appKey = "<YOUR_APP_KEY>")
    }
}

Initialize once in Application.onCreate. No additional permissions in the manifest — INTERNET is sufficient. The SDK does not request AD_ID, does not query AdvertisingIdClient, and does not declare ACCESS_NETWORK_STATE.

Track the event in Kotlin (Android)

kotlin Respectlytics
import com.respectlytics.android.Respectlytics

fun reportLevelComplete(levelNumber: Int) {
    if (levelNumber <= 50) {
        Respectlytics.track("level_complete_$levelNumber")
    } else {
        val bucket = ((levelNumber - 1) / 50) * 50 + 1
        Respectlytics.track("level_complete_${bucket}_${bucket + 49}")
    }
}

Use the same logic for level_failed_N events — they tell you where the difficulty walls are.

Privacy & implementation notes

Per-player progression — score curves, leaderboard positions, skill ratings, items inventory — lives in your game backend. That's where it has authority and where the gameplay logic reads from. Mirroring it into product analytics duplicates a system of record without adding signal you can't get from the game backend's own reporting.

Most game-product decisions are about rate ("is this level a difficulty wall for new players?") rather than skill ("how quickly does this player progress?"). The rate question is session-grouped. The skill question is per-player and lives in your game backend.

Many teams discover the com.google.android.gms.permission.AD_ID permission in their merged manifest only after Google Play flags them — usually because a transitive dependency dragged it in. Respectlytics's Kotlin SDK has no Google Play Services dependency at all, so it cannot contribute to that merge.

The SDK is implemented as pure Kotlin coroutines with no Java sources, no RxJava, and no platform channels. Events are queued in a Channel<Event> buffered to a small ring (RAM-only), drained by a coroutine that flushes every 30 seconds or on backgrounding. There is no SharedPreferences usage.

How this compares to other analytics SDKs

Level complete eventFirebase AnalyticsMixpanelGameAnalyticsRespectlytics
Level number as parameterYesYesYesUse distinct event_name
Score storedRecommendedRecommendedYesOut of scope (game backend)
Attempts to completeRecommendedRecommendedYesOut of scope (game backend)
Time-to-completeRecommendedRecommendedYesServer-side derivation
Per-player progression curveYesYesYesOut of scope (game backend)

Frequently asked questions

Won't we run out of distinct event names if we have 1000 levels?

For high-level-count games, bucket: level_complete_1_50, level_complete_51_100, etc. The Respectlytics aggregation handles either shape. Per-level granular progression is a game-backend signal — your leaderboards and skill-rating system already have it.

How do we measure difficulty without per-player attempts?

Per-session. The rate of sessions where level_complete_N follows level_complete_N-1 is your level-N completion rate. Country-bucketed and device-platform-bucketed shows where the difficulty cliff sits.

What about retries?

Distinct event name: level_failed_N. The rate of level_failed_N to level_complete_N over a session is your level-N retry signal.

Should we instrument every level individually?

If you have under 50 levels, yes — distinct event names per level. Past that, bucket. The principle is to keep your event-name taxonomy navigable; 1000 distinct level event names is functionally a parameter.

Related guides

Track what matters. Collect nothing you don't.

Five-field event schema, RAM-only event queue, no IDFA, no AAID, no persistent user IDs. Helps developers avoid collecting personal data in the first place.