Respectlytics Respect lytics
Menu
Kotlin (Android) Video completion Privacy-first

How to track video completions in Kotlin (Android) without personal data

Video engagement events — play, 25% / 50% / 75% milestones, complete — are dense signals for content apps. Most analytics SDKs default to tagging them with the video ID, content category, and watch duration, building a per-user watch history along the way. Respectlytics helps developers avoid collecting personal data in the first place: in Kotlin (Android), each milestone is its own named event, with no metadata. Below: how to wire to the player's progress callbacks, why milestones beat raw position, and what content metadata stays in your CMS.

Fire the call at meaningful playback milestones: video_play_started, video_25pct_reached, video_50pct_reached, video_75pct_reached, video_completed. Don't pass video ID, content title, 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 androidx.media3.exoplayer.ExoPlayer
import com.respectlytics.android.Respectlytics

class VideoMilestoneTracker(private val player: ExoPlayer) {
    private var fired25 = false; private var fired50 = false
    private var fired75 = false; private var firedComplete = false

    fun tick() {
        val duration = player.duration.takeIf { it > 0 } ?: return
        val progress = player.currentPosition.toFloat() / duration
        if (progress >= 0.25f && !fired25) { fired25 = true; Respectlytics.track("video_25pct_reached") }
        if (progress >= 0.50f && !fired50) { fired50 = true; Respectlytics.track("video_50pct_reached") }
        if (progress >= 0.75f && !fired75) { fired75 = true; Respectlytics.track("video_75pct_reached") }
        if (progress >= 0.99f && !firedComplete) { firedComplete = true; Respectlytics.track("video_completed") }
    }
}

Call tick() from a 1-second Handler post-loop while the video is playing. Reset flags on onMediaItemTransition.

Privacy & implementation notes

Tracking exact playback position generates 30+ events per minute of viewing and dilutes every other signal in your analytics pipeline. Four milestones (25, 50, 75, 100%) per video carry essentially the same product signal at 1/100th the volume.

Your CMS already stores video titles, descriptions, durations, and content categories. Joining video_completed_movie events to your CMS's view-count-per-video data answers most product questions; storing the title as analytics metadata duplicates a system of record without adding signal.

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

Video completion eventFirebase AnalyticsMixpanelRespectlytics
Video ID as parameterRecommendedRecommendedForbidden (use top-N event_names)
Content category / genreRecommendedRecommendedUse distinct event_name (top-N)
Watch duration in secondsRecommendedRecommendedOut of scope (use milestones)
Per-user watch historyYesYesOut of scope (use content backend)
Completion *rate* by country / platformYesYesYes (default)

Frequently asked questions

How do we know which videos are most-watched?

Your content backend already has play counts per video — that's where video-level engagement lives. Respectlytics is for product engagement at the feature level ("is the video player surface working?"). Conflating them duplicates a system of record.

Should we track exact playback position?

No — milestones (25 / 50 / 75 / 100%) carry the signal. Exact position produces a flood of low-value events that drown the milestone signal. Player-side analytics (your video player vendor's SDK) has its own per-second tracking if you genuinely need it.

What about content category breakdowns (movies, shows, shorts)?

If you have a small fixed set, distinct event names: video_completed_movie, video_completed_show. For high-cardinality categorization, use your content backend's analytics — it has the metadata you need.

Does this work for live streams?

Conceptually different. Use distinct event names — livestream_joined, livestream_left — and fire on join and leave. Milestones don't apply to live; duration is computed from join → leave timestamp difference.

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.