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).
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.Configurationon initialize — check your public key matchespub_(test|live)_...; asec_...secret key or a malformed string is rejected client-side.- Requests fail from an emulator against localhost — use
10.0.2.2instead oflocalhost(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.