Dashboard

Android SDK

io.sentriq:sentriq-android is the native Kotlin client SDK for Android — a thin wrapper over the same public-key POST /v1/events ingestion contract @sentriq/browser uses. It never invents a new backend endpoint, auth scheme, or event schema. Targets minSdk 24 (Android 7.0) and above, Kotlin coroutines, distributed as an AAR.

Requirements

  • Android API level 24 (Android 7.0) or higher
  • Kotlin with coroutines

Gradle setup

Not yet published to Maven Central — install via mavenLocal() against a checked-out commit of this repository until a release process exists (run ./gradlew :sentriq:publishToMavenLocal from sdks/android):

// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        mavenLocal()
        google()
        mavenCentral()
    }
}
// app/build.gradle.kts
dependencies {
    implementation("io.sentriq:sentriq-android:0.1.0")
}

Initialize

import io.sentriq.android.Sentriq
import io.sentriq.android.SentriqOptions

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Sentriq.initialize(this, SentriqOptions(publicKey = "pub_test_xxx"))
    }
}

Call this once, typically in your Application.onCreate. Only your application context is ever retained — never an Activity. Safe to call again later (the latest call wins).

Never embed a secret key in a mobile app
Anything inside a shipped APK/AAB is public. SentriqOptions has no secret-key field at all; passing one throws SentriqError.Configuration immediately.

Track an event

import io.sentriq.android.Sentriq
import io.sentriq.android.SentriqAccountReference
import io.sentriq.android.SentriqEventType
import kotlinx.coroutines.launch

lifecycleScope.launch {
    try {
        val result = Sentriq.track(
            SentriqEventType.LOGIN,
            account = SentriqAccountReference(id = userId),
            screenName = "login-screen",
        )
        // result.risk.decision: "allow" | "monitor" | "challenge" | "block"
    } catch (e: Exception) {
        // SentriqError.Network, .Timeout, .Api, .Validation, ...
    }
}

track is a suspend fun — call it from a coroutine (a lifecycleScope.launch, viewModelScope.launch, or any otherCoroutineScope). It always runs its network work on Dispatchers.IO internally, so it never blocks your calling thread, including the main thread. screenName is optional and analogous to a browser's window.location.pathname: it becomes part of a synthetic app:// URL sent as page.url, since a native app has no real page URL concept.

Account association

Pass account = SentriqAccountReference(id = ...) to track() once you know who the current user is — your own opaque reference for them, never a password or email. Omit it entirely for anonymous events (e.g. a pre-login page view).

Coroutines and threading

Never call track() from the main thread expecting it to block synchronously — it's a suspend function. Multiple concurrent track() calls from different coroutines are safe: internal session state is protected by a Mutex, not left to the caller to serialize.

Device, session, and installation identity

Handled automatically — you never construct these yourself. An SDK-generated, random installation identifier (never a hardware ID) persists in a private SharedPreferences file across app launches and resets on reinstall or when the user clears app data. A session identifier renews after 30 minutes of inactivity, tracked via a lightweight Application.ActivityLifecycleCallbacks observer. The full field-by-field detail lives in docs/architecture/MOBILE_SDK_FOUNDATION.md in the repository.

Error handling

when (val error = e) {
    is SentriqError.Configuration -> // bad SDK usage — fix your code
    is SentriqError.Network -> // couldn't reach the server
    is SentriqError.Timeout -> // request timed out
    is SentriqError.Cancelled -> // the enclosing coroutine was cancelled
    is SentriqError.Decoding -> // unexpected response shape
    is SentriqError.Api -> // error.status, error.code, error.requestId
    is SentriqError.Validation -> // error.fields (field -> messages)
}

Custom API URL (local/dev)

SentriqOptions(publicKey = "pub_test_xxx", apiUrl = "http://10.0.2.2:8000")

10.0.2.2 is the Android emulator's alias for your host machine's localhost; on a physical device use a real reachable LAN address instead.

Permissions

This SDK declares exactly one permission — android.permission.INTERNET (normal protection level, no runtime prompt, granted automatically). No other permission, dangerous or otherwise, is added to your app's merged manifest.

Privacy

This SDK never reads IMEI, MAC address, hardware serial number, Android ID as a cross-app identifier, or any advertising identifier. It collects only permission-free device signals (locale, timezone, screen metrics, CPU/memory counts) plus the SDK-generated installation and session identifiers described above.

Emulator and device notes

Works identically on an emulator and a real device. Use 10.0.2.2 (see above) only when pointing the SDK at a locally-running API from an emulator.

Troubleshooting

  • SentriqError.Configuration on initialize — check your public key matches pub_(test|live)_...; a sec_... secret key or a malformed string is rejected client-side.
  • Requests fail from an emulator against localhost — use 10.0.2.2 instead of localhost (see above).

Limitations

  • No React Native, Flutter, or Expo wrapper — this is the native Kotlin SDK only.
  • No optional browser-only signals (private-browsing detection, DOM interaction telemetry) — no equivalent concept exists on Android.