Respectlytics Respect lytics
Menu
Kotlin (Android) Push notification opt-in Privacy-first

How to track push notification opt-in in Kotlin (Android) without personal data

Push opt-in rate is one of the highest-leverage signals in mobile analytics: it gates every re-engagement channel you have. Respectlytics helps developers avoid collecting personal data in the first place: the opt-in event in Kotlin (Android) is just `push_optin` — no device token, no APNs identifier, no Firebase Cloud Messaging registration ID. Below: how to wire the call into the system permission callback on Kotlin (Android), how to A/B-test prompt timing without per-user assignment, and the trade-off you make by not storing a per-user opt-in state.

Wire the call into the same callback that receives the system permission result. On success, emit `push_optin`. If you also want to measure declines (most teams do), emit `push_decline` on the negative result. Don't pass the device token or any registration ID as metadata — your push backend already has those.

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.activity.result.contract.ActivityResultContracts
import com.respectlytics.android.Respectlytics

class MainActivity : ComponentActivity() {
    private val pushPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { granted ->
        Respectlytics.track(if (granted) "push_optin" else "push_decline")
    }

    private fun requestPushPermission() {
        if (Build.VERSION.SDK_INT >= 33) {
            pushPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
        } else {
            // Pre-Android 13: notifications are enabled by default; emit a synthetic optin.
            Respectlytics.track("push_optin_implicit_pre_android_13")
        }
    }
}

On Android 13+ you must request `POST_NOTIFICATIONS`. On older versions, permission is granted by default — emit a separate event name (or skip) so you can distinguish the two cases in analytics.

Privacy & implementation notes

Your push-delivery backend (APNs, FCM, OneSignal, Pusher Beams, etc.) already has the device push token — that's where it lives operationally. Sending it to Respectlytics would just be re-creating a per-user dataset you've already built elsewhere. The API rejects it.

Most push-related product decisions are about *rate*, not *state*: "does moving the prompt to post-onboarding move our DE iOS opt-in rate?". You don't need per-user opt-in state in your analytics pipeline to answer that — you need session-grouped event counts.

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

Push opt-in event Firebase Analytics Mixpanel Respectlytics
Device push token stored Yes (FCM token in user property) Yes (in profile) Never
Per-user opt-in state Yes Yes Out of scope (your push backend tracks this)
Prompt timing as event property Recommended Recommended Use distinct event_name
iOS provisional opt-in distinguishable Manual (via param) Manual (via property) Use distinct event_name (push_provisional_optin)
Opt-in rate by country / platform Yes Yes Yes (default aggregation)

Frequently asked questions

How do we know which users opted in if we don't store user state?

You don't, in your analytics. Your push-delivery backend (APNs, FCM, OneSignal, etc.) already has the authoritative opt-in state per device token — that's its job. Respectlytics tells you the opt-in *rate* over sessions, which is the metric you optimize for product decisions.

Can we A/B-test opt-in prompt timing?

Yes — emit distinct event names per prompt-timing variant: `push_optin_at_onboarding`, `push_optin_post_first_session`, etc. The aggregation gives you per-variant conversion rate without any per-user assignment stored.

Should we track when the prompt was *shown* in addition to the result?

Yes, with two event names: `push_prompt_shown` and `push_optin` (or `push_decline`). The session-grouped count gives you prompt → grant rate directly.

What about iOS 15+ provisional authorization?

If you use `provisional` authorization (which doesn't show a prompt and delivers quietly until the user upgrades to full), emit `push_provisional_optin` as a distinct event. It's a different state with different downstream implications, so don't conflate it with the explicit opt-in event.

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.