Tapemetric

SDKs

Android (Kotlin / Java)

Native Android SDK for phones, tablets, and Android TV. Works with ExoPlayer, Media3, and any custom player.

Requirements

  • Android API 21+ (Lollipop, 5.0+)
  • Kotlin 1.9+ recommended; Java 17 also supported
  • AndroidX

Install — Gradle

In your app module’s build.gradle:

bash
dependencies {
    implementation 'com.tapemetric:analytics:1.0.0'
}

Or in build.gradle.kts:

kotlin
dependencies {
    implementation("com.tapemetric:analytics:1.0.0")
}

Version catalog (libs.versions.toml)

bash
[versions]
tapemetric = "1.0.0"

[libraries]
tapemetric-analytics = { module = "com.tapemetric:analytics", version.ref = "tapemetric" }

Permissions

The SDK declares its required permissions in its own manifest, so no changes to your AndroidManifest.xml are needed:

  • INTERNET — required to send events
  • ACCESS_NETWORK_STATE — used to skip flushes when offline

The SDK never requests location, contacts, advertising IDs, phone state, or device identifiers.

Initialize

Configure once in your Application class:

kotlin
import android.app.Application
import com.tapemetric.analytics.Tapemetric
import com.tapemetric.analytics.TapemetricConfig

class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        Tapemetric.configure(
            this,
            TapemetricConfig(
                apiKey = "tm_live_yourkey",
                debug = BuildConfig.DEBUG
            )
        )
    }
}

Don’t forget to register your Application in AndroidManifest.xml:

text
<application
    android:name=".MyApp"
    ... >
</application>

Identify users

kotlin
import com.tapemetric.analytics.PlanType

// After login
Tapemetric.identify(
    userId = "user_123",
    plan = PlanType.SVOD,
    traits = mapOf("country" to "IN")
)

// On logout
Tapemetric.reset()

Track playback

kotlin
import com.tapemetric.analytics.TrackContent
import com.tapemetric.analytics.PlaybackState

val content = TrackContent(
    contentId = "aashiqana_s04e12",
    contentType = "series",
    contentTitle = "Aashiqana",
    season = 4,
    episode = 12
)

// On play
Tapemetric.trackPlayStart(content, PlaybackState(positionSec = 0.0))

// On pause / resume
Tapemetric.trackPause(PlaybackState(positionSec = 124.5))
Tapemetric.trackResume(PlaybackState(positionSec = 124.5))

// On bitrate change
Tapemetric.trackBitrateChange(2400, PlaybackState(positionSec = 200.0, bitrateKbps = 2400))

// On end
Tapemetric.trackComplete(PlaybackState(positionSec = 1320.0, durationSec = 1320.0))

// On error
Tapemetric.trackError(code = "NETWORK_ERROR", message = "Failed to load manifest", fatal = true)

Track revenue

kotlin
Tapemetric.trackPurchase(
    amountInr = 199.0,
    plan = PlanType.SVOD,
    orderId = "order_xyz",
    contentId = "pvr_premiere_2026"  // optional, for TVOD
)

ExoPlayer integration

kotlin
val exoPlayer = ExoPlayer.Builder(this).build()
val content = TrackContent(contentId = "aashiqana_s04e12", contentType = "series")
var bufferStartedAt: Long = 0

exoPlayer.addListener(object : Player.Listener {
    override fun onPlaybackStateChanged(state: Int) {
        val pos = exoPlayer.currentPosition / 1000.0
        when (state) {
            Player.STATE_READY -> Tapemetric.trackPlayStart(content, PlaybackState(pos))
            Player.STATE_BUFFERING -> bufferStartedAt = System.currentTimeMillis()
            Player.STATE_ENDED -> Tapemetric.trackComplete(PlaybackState(pos))
        }
    }

    override fun onIsPlayingChanged(isPlaying: Boolean) {
        val pos = exoPlayer.currentPosition / 1000.0
        if (isPlaying && bufferStartedAt > 0) {
            // Buffer ended
            Tapemetric.trackBuffer(
                durationMs = (System.currentTimeMillis() - bufferStartedAt).toInt(),
                playback = PlaybackState(pos)
            )
            bufferStartedAt = 0
        }
        if (isPlaying) Tapemetric.trackResume(PlaybackState(pos))
        else Tapemetric.trackPause(PlaybackState(pos))
    }

    override fun onPlayerError(error: PlaybackException) {
        Tapemetric.trackError(
            code = error.errorCodeName,
            message = error.message ?: "unknown",
            fatal = true
        )
    }
})

Java interop

Every public API is annotated @JvmStatic, so calling from Java is the same minus Kotlin defaults:

bash
import com.tapemetric.analytics.Tapemetric;
import com.tapemetric.analytics.TapemetricConfig;
import com.tapemetric.analytics.TrackContent;
import com.tapemetric.analytics.PlaybackState;

Tapemetric.configure(this, new TapemetricConfig("tm_live_yourkey",
    "https://ingest.tapemetric.com", 10000L, 50, false));

TrackContent content = new TrackContent(
    "aashiqana_s04e12", "series", "Aashiqana", 4, 12
);
Tapemetric.trackPlayStart(content, new PlaybackState(0.0, null, null));

Android TV

The SDK auto-detects Android TV via UI_MODE_TYPE_TELEVISION and reports device_type as tv on every event. No extra config required.

ProGuard / R8

The SDK ships its own consumer-rules.pro — your app’s ProGuard config doesn’t need any Tapemetric-specific keep rules.

Calling Tapemetric.configure() registers a ProcessLifecycleOwner observer that flushes events when the app goes to background. This means events make it to our servers even if the user kills the app from the recents tray.

Source and issues

SDK source: github.com/tapemetric/analytics-android. File issues there or email sdk@tapemetric.com.