Skip to content
Documentation

Documentation

Android (Kotlin)

Add the SDK, initialise it, sign beacons, bind purchases, forward deep links. Twenty minutes.

Requirements

  • Android 5.0+ (minSdk 21). The dependency resolves from Maven Central, which every Android project already has.
  • The SDK declares two small Play Services artifacts itself (advertising id and App Set ID) and merges the AD_ID permission and the Install Referrer receiver into your manifest. No manual manifest work.
  • Play Console → App content → Data safety must declare that the app collects the Advertising ID. That declaration is per app, not per release track, so it touches your live listing; get it agreed before the first upload that contains the SDK.

1. Add the dependency

kotlin
// app/build.gradle.kts
dependencies {
    implementation("com.roassensor:roas:0.1.6")
}
Copy the exact line from SDK integration in the panel; it pins the current version.

2. Initialise

Once, as early as possible: Application.onCreate() is the right place. Initialising in a deep screen risks missing the earliest events of the one visit that matters most, the open right after an ad click.

kotlin
import com.roassensor.sdk.Roas

class App : Application() {
    override fun onCreate() {
        super.onCreate()
        Roas.initialize(
            this,
            publicKey = "YOUR_PUBLIC_KEY",
            baseUrl = "https://api.roassensor.com",
            appSecret = BuildConfig.ROAS_APP_SECRET.ifEmpty { null },
        )
    }
}

baseUrl is printed by the panel and must be passed explicitly. The SDK's built-in default is the production host; an app pointed at the wrong environment compiles, runs, and posts every beacon into a void with nothing to say so.

initialize is idempotent: a second call is ignored, so calling it from more than one entry point is harmless.

3. The app secret

It comes from a build-time variable, never a source file. Put it in local.properties (gitignored) or your CI's secret store, and expose it as a BuildConfig field:

kotlin
// app/build.gradle.kts
val roasAppSecret: String = (project.findProperty("ROAS_APP_SECRET") as String?)
    ?: System.getenv("ROAS_APP_SECRET")
    ?: ""

android {
    buildFeatures { buildConfig = true }
    defaultConfig {
        buildConfigField("String", "ROAS_APP_SECRET", "\"$roasAppSecret\"")
    }
}
local.properties: ROAS_APP_SECRET=…. Gradle reads it as a project property.

The .ifEmpty { null } in the initialise call is not cosmetic: an empty string signs with an empty key and every beacon is rejected as invalid. Absent must mean null.

4. Bind purchases to the install

Play's purchase notification names the purchase token and the event, not the buyer. The one field that carries our visitor id through a purchase is obfuscatedAccountId on the billing flow. Set it, or every sale books as unattributed.

kotlin
val vid = Roas.visitorId()  // "rs" + 32 hex, or null before initialize()

val flowParams = BillingFlowParams.newBuilder()
    .setProductDetailsParamsList(productDetailsParamsList)
    .apply { vid?.let { setObfuscatedAccountId(it) } }
    .build()
billingClient.launchBillingFlow(activity, flowParams)

Verify it once on an internal build: the Purchase object Play returns carries accountIdentifiers.obfuscatedAccountId. If it is null, the field was not set.

Optional, 0.1.8+: report the purchase the moment Billing settles it, instead of waiting for Play's notification. The SDK only ever names the purchase; the amount is read from Google. It dedupes against the notification that follows, so nothing double-books.

kotlin
// In your PurchasesUpdatedListener, once purchaseState == PURCHASED
Roas.verifyPurchase(purchase.purchaseToken, productId, isSubscription = true)

Revenue itself comes from Play's notifications; see Google Play revenue.

6. Identity and funnel events

Play gives us no buyer email, so without this the visitor id is the single thread between a sale and its campaign. Call identify whenever an email becomes known; it is idempotent.

kotlin
Roas.identify(email = user.email)                       // hashed on device
Roas.track(RoasEvent.BEGIN_CHECKOUT, properties = mapOf(
    RoasProps.PRODUCT_ID to "pro_yearly",                    // must equal the Play product id
))

RoasEvent covers the usual funnel: VIEW_CONTENT, ADD_TO_CART, BEGIN_CHECKOUT, SIGN_UP, LOGIN, START_TRIAL, SUBSCRIBE, LEVEL_START, LEVEL_COMPLETE and more. RoasEvent.CUSTOM with a name covers anything else. Hashed email also raises match rates for the conversions we push back to Meta and Google Ads.

7. Debugging

kotlin
Roas.setLogLevel(RoasLogLevel.DEBUG)   // logcat tag: RoasSensor
Roas.setOnDeliveryResult { path, ok, error ->
    Log.d("roas", "$path -> ${if (ok) "ok" else error}")
}

A healthy first launch logs /api/tracking/mobile/first-open -> HTTP 201. The Install Referrer line beside it says where the install came from (play=OK, OK_ORGANIC, or an OEM store on Vivo, Huawei, Xiaomi and Samsung devices, which the SDK also reads).

Check it worked

  1. Install the build on a device and open it once.
  2. On the app's Overview tab, an install row appears within seconds with sdk_version matching your dependency and signed = true.
  3. A build installed with adb or from Android Studio shows an organic referrer. That is expected: only a Play-mediated install carries a referrer. Testing a mobile integration covers the real thing.