Browse Developers
Android SDK
kontala-android captures what people do in your native Android app, built with Views or Jetpack Compose, in Kotlin or Java, the way the website tag captures a site.
It records every screen they see, every control they tap, who signs in, what forms and searches sent, how far they scrolled, where they struggled, how fast each screen was, and your app's own events.
Requirements
- Android 7.0 (API 24) or later.
- An app compiled against Android 16 (API 36) or later, as Google Play asks of every app update.
- For a Kotlin app, Kotlin 2.0 or later. A Java app needs nothing more.
The SDK reads AndroidX Fragments, Jetpack Navigation and Jetpack Compose where your app has them, and needs none of them.
1. Find your write key
Open the property in Kontala and find its Events API card.
It shows the property's write key (pk_live_...) and the Events API URL to send to.
The write key is public: every copy of your app carries it, as every page carries the tag's.
Never put a server key (ksk_live_...) in an app.
2. Add the library
Add it to your app module's build.gradle.kts:
dependencies {
implementation("la.konta:kontala-android:0.1.0")
}
It is on Maven Central, and its two dependencies, Google Play services' advertising ID and App Set ID libraries, are on Google's repository.
Both are the repositories Android Studio puts in every new project's settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
}
}
3. Start it
Call Kontala.init in your Application.onCreate, before any activity:
class ShopApp : Application() {
override fun onCreate() {
super.onCreate()
Kontala.init(this, writeKey = "pk_live_...", endpoint = "https://konta.la/v1/events")
}
}
public class ShopApp extends Application {
@Override public void onCreate() {
super.onCreate();
Kontala.init(this, "pk_live_...", "https://konta.la/v1/events");
}
}
The application class is the one your manifest names (<application android:name=".ShopApp">).
Use the write key and endpoint from your property's Events API card.
Activities, fragments and Navigation destinations are screens by themselves, so nothing else is configured per screen.
Every other Kontala method may be called from any thread, and before init: those calls are kept and applied in order.
4. Platform setup
No setup is needed.
The SDK adds the INTERNET permission and the AD_ID permission, so declare advertising ID use in Play Console.
If your app must not use the advertising ID, for example in Google Play's Families programme, remove the permission in your own manifest:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove" />
</manifest>
R8
The SDK's own R8 rules come with it, so a minified app needs none for it to work. To name screens by their class in a minified build, where nothing else names them, keep the class names:
-keepnames class * extends android.app.Activity
-keepnames class * extends androidx.fragment.app.Fragment
5. Check that it works
Start the app with debug logging on:
Kontala.init(
this,
writeKey = "pk_live_...",
endpoint = "https://konta.la/v1/events",
options = KontalaOptions(debug = true),
)
The log, under the tag Kontala (adb logcat -s Kontala), shows every event as it is captured, and every event the API refused, with its reason.
A debuggable build, and any build in an emulator, marks its events as a developer's. Kontala leaves them out of visitor counts by default, as it does for a development server.
Optional setup
Name screens and mark controls
| On | Marker | Does |
|---|---|---|
| An activity or a fragment | KontalaScreen | Declares the screen it shows: its name, page type and data |
| A view | view.kontalaEvent = KontalaClickType.ADD_TO_CART | Declares the click type of a control, or of every control in it |
| A view | view.kontalaMask = true | A tap in it is sent as its role alone: no id, label, text or class |
| A view | view.kontalaIgnore = true | Nothing in it is captured |
| A composable | Modifier.kontalaEvent(...), .kontalaMask(), .kontalaIgnore() | The same, for what the composable draws |
A Compose app using Navigation hands its controller over with KontalaNavigation.track(navController).
A text field never sends what is typed into it.
Identify signed-in users
Kontala.identify(user = "customer-42", org = "acme", traits = mapOf("plan" to "pro"))
Every later event carries the user and organisation.
Call Kontala.identify() with neither to log out, or Kontala.reset() to also forget the install and session.
Track your own events
Kontala.track("wishlisted", mapOf("product_id" to "42"))
Custom events arrive while the property's data layer captures the Events API's events.
Consent
The SDK starts with consent granted. To wait for your consent banner instead, start undecided and answer when the visitor chooses:
Kontala.init(
this,
writeKey = "pk_live_...",
endpoint = "https://konta.la/v1/events",
options = KontalaOptions(consent = KontalaConsent.UNKNOWN),
)
// Later, from your banner:
Kontala.consent(KontalaConsent.GRANTED)
Kontala.optOut(true) stops all capture, sending and storage on the device, across launches.
See Consent for how Kontala treats each state.
Web pages in the app
If your app shows pages of your website in a WebView, the SDK shares its visitor and visit with them through the WebView cookie store, so one person is counted once. Attach a WebView for the closest link: the page then takes the app's consent and signed-in user.
class HelpActivity : Activity() {
private lateinit var attachment: KontalaWebViewAttachment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val webView = WebView(this).apply { settings.javaScriptEnabled = true }
attachment = Kontala.attachWebView(webView)
setContentView(webView)
webView.loadUrl("https://shop.example.com/help")
}
override fun onDestroy() {
attachment.detach()
super.onDestroy()
}
}
Attach a WebView before its first page loads, and call detach() when it goes.
Troubleshooting
- Screens are missing from a release build. R8 renamed their classes: declare each with
KontalaScreen, or keep the class names (see R8). - Every screen is the one activity. A single-activity app names its screens through Navigation,
KontalaNavigation.track, orKontala.screen. - No taps arrive. Turn clicks on for the property in Kontala.
- No custom events arrive. The property's data layer is off, or its capture rules do not include the Events API.
- Every row reads as a developer's. It is a debuggable build, or an emulator.
- Nothing arrives at all. The write key is wrong or revoked, or the app cannot reach the endpoint. Events wait in the app's queue until it can.
Google Play data safety form
What the SDK sends is yours to declare in Google Play's data safety form:
| Data safety form | What the SDK sends |
|---|---|
| Device or other IDs | A random install id; on consented events, Android ID, App Set ID and advertising ID |
| Personal info: User IDs | The ids Kontala.identify names |
| Personal info: Email address | The email a login names |
| App activity: App interactions | Screens, taps, swipes, scroll depth, time on screen and your events |
| App activity: In-app search history | What a search looked for |
| App activity: Other user-generated content | What your app's forms sent |
| App info and performance: Crash logs | Exceptions your app did not catch |
| App info and performance: Diagnostics | Rage and dead taps |
| App info and performance: Other app performance data | How fast each screen was |
| Location: Approximate location | Country, region and city, derived from each request's address |
Each is used for analytics and linked to the user where your app identifies them. None is used for advertising: the advertising ID goes to Kontala alone and is never joined with another company's data.
Licence
The SDK is provided under the Kontala SDK License.