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()
}Handle Deep Links in MainActivity
MainActivityclass 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?