Tracking in Android Applications

This guide shows how to integrate Roivenue Tracking in an Android app using Kotlin. It sends lightweight JSON events (pageview, conversion) with device and session UTM data.

Prerequisites

Android Studio Hedgehog (2024+)

Kotlin 1.9+

Min SDK 24+

Internet permission in AndroidManifest.xml

<uses-permission android:name="android.permission.INTERNET"/>

What the Tracking API Expects

The Roivenue endpoint accepts events in JSON format. Each event represents a user interaction and includes basic context (timestamp, device info, URL, etc.) plus optional conversion data.

{
  "propertyId": "adbfb3aa-xxxx-xxxx-xxxx-xxxxxxxx",
  "type": "pageview",
  "timestamp": "2024-10-30T07:50:40",
  "url": "https://example.com/?utm_source=facebook",
  "deviceId": "20904e-f1e3-79f2-b5b7-00d6a7bfe57e",
  "userId": "someUserHashed",
  "referer": "$direct",
  "screenWidth": 1170,
  "screenHeight": 2532,
  "utmSource": "facebook",
  "utmMedium": "cpc",
  "utmCampaign": "autumn_sale",
  "conversionType": "purchase",
  "conversionId": "ORD-001",
  "conversionValue": 149.99,
  "currencyCode": "EUR"
}

Capturing and Managing UTMs

UTMs describe the source of the current session. They are cleared on app cold start and refreshed when the app is opened via a new deep link.

package com.roivenue.tracking

import android.net.Uri

object UTMManager {
    private val utmKeys = listOf(
        "utm_source", "utm_medium", "utm_campaign", "utm_content", "utm_term"
    )

    private val sessionUTMs = mutableMapOf<String, String>()

    fun storeFromUri(uri: Uri) {
        for (key in utmKeys) {
            uri.getQueryParameter(key)?.let { value ->
                sessionUTMs[key] = value
            }
        }
    }

    fun getAll(): Map<String, String> = sessionUTMs.toMap()

    fun clear() = sessionUTMs.clear()
}
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        UTMManager.clear() // reset on fresh launch
        handleIntent(intent)
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        handleIntent(intent)
    }

    private fun handleIntent(intent: Intent?) {
        val data = intent?.data ?: return
        UTMManager.storeFromUri(data)
    }
}

DeviceId generation

Generate a device ID compliant with Google Play and App Tracking Transparency rules.

package com.roivenue.tracking

import android.content.Context
import java.util.*

object DeviceIdManager {
    private const val PREF_NAME = "roivenue_prefs"
    private const val PREF_KEY = "roivenue_device_id"

    fun getOrCreate(context: Context): String {
        val prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
        var id = prefs.getString(PREF_KEY, null)
        if (id == null) {
            id = UUID.randomUUID().toString()
            prefs.edit().putString(PREF_KEY, id).apply()
        }
        return id
    }
}

RoivenueTracker.kt

package com.roivenue.tracking

import android.content.Context
import android.util.DisplayMetrics
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.json.JSONObject
import java.net.HttpURLConnection
import java.net.URL
import java.text.SimpleDateFormat
import java.util.*

class RoivenueTracker(private val context: Context) {
    private val endpoint = "https://tr.roivenue.com/c"
    private val propertyId = "adbfb3aa-xxxx-xxxx-xxxx-xxxxxxxx"
    private val deviceId = DeviceIdManager.getOrCreate(context)

    fun trackEvent(
        type: String,
        url: String? = null,
        userId: String? = null,
        conversionType: String? = null,
        conversionId: String? = null,
        conversionValue: Double? = null,
        currencyCode: String? = null
    ) {
        val metrics: DisplayMetrics = context.resources.displayMetrics
        val utms = UTMManager.getAll()

        val payload = JSONObject().apply {
            put("propertyId", propertyId)
            put("type", type)
            put("timestamp", isoTimestamp())
            put("url", url)
            put("deviceId", deviceId)
            put("userId", userId)
            put("screenWidth", metrics.widthPixels)
            put("screenHeight", metrics.heightPixels)
            put("conversionType", conversionType)
            put("conversionId", conversionId)
            put("conversionValue", conversionValue)
            put("currencyCode", currencyCode)
            put("utmSource", utms["utm_source"] ?: "\$direct")
            put("utmMedium", utms["utm_medium"])
            put("utmCampaign", utms["utm_campaign"])
            put("utmContent", utms["utm_content"])
            put("utmKeyword", utms["utm_term"])
        }

        sendEvent(payload)
    }

    private fun isoTimestamp(): String {
        val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss", Locale.US)
        format.timeZone = TimeZone.getTimeZone("UTC")
        return format.format(Date())
    }

    private fun sendEvent(json: JSONObject) {
        CoroutineScope(Dispatchers.IO).launch {
            try {
                val url = URL(endpoint)
                val conn = url.openConnection() as HttpURLConnection
                conn.requestMethod = "POST"
                conn.setRequestProperty("Content-Type", "application/json")
                conn.doOutput = true
                conn.outputStream.use { it.write(json.toString().toByteArray()) }
                conn.responseCode
                conn.disconnect()
            } catch (_: Exception) { }
        }
    }
}

Tracking Page Views

// Inside Activity or Fragment
override fun onResume() {
    super.onResume()
    RoivenueTracker(this).trackEvent(
        type = "pageview",
        url = "app://home"
    )
}

Tracking Conversions

RoivenueTracker(this).trackEvent(
    type = "conversion",
    conversionType = "purchase",
    conversionId = "ORDER-001",
    conversionValue = 149.99,
    currencyCode = "EUR"
)

Last updated

Was this helpful?