Skip to main content
The AdTracker API allows you to track video advertisement events. Use this to measure ad views, engagement, clicks, and completions within video content.

Overview

AdTracker provides comprehensive video ad tracking:
  • Automatic lifecycle tracking - Tracks VideoAdView and VideoAdCompletion events
  • Engagement metrics - Measures time spent watching ads
  • Click tracking - Tracks ad interactions
  • Custom events - Track custom ad-related events
  • Shared context - Can share View ID with parent MediaTracker

Creating an AdTracker

AdTracker can be created in two ways: Use this when the ad is related to video content currently being viewed. The AdTracker will share the same View ID with the MediaTracker:
val videoTracker = permutive.trackVideoView(...)

val adTracker = videoTracker.trackAdView(
    durationMs = 15000,  // 15 second ad
    adProperties = AdTracker.AdProperties(
        title = "Product Advertisement",
        durationInSeconds = 15,
        isMuted = false,
        campaignId = "camp_123",
        creativeId = "creative_456"
    )
)

2. Standalone AdTracker

Use this for standalone video ads not associated with specific video content:
val adTracker = permutive.trackVideoAdView(
    durationMs = 30000,  // 30 second ad
    adProperties = AdTracker.AdProperties(
        title = "Standalone Ad",
        durationInSeconds = 30,
        isMuted = false,
        campaignId = "camp_789",
        creativeId = "creative_012"
    )
)

Ad Events

AdTracker automatically generates these events:

1. VideoAdView Event

Tracked when AdTracker is created:
Event: VideoAdView
Properties:
  - [ad properties]
  - [custom properties]

2. VideoAdCompletion Event

Tracked when completion() is called:
Event: VideoAdCompletion
Properties:
  - engaged_time: Time spent watching ad (seconds)
  - completion: Percentage of ad viewed (0.0 - 1.0)
  - [ad properties]
  - [custom properties]

3. VideoAdClicked Event

Tracked when clicked() is called:
Event: VideoAdClicked
Properties:
  - [ad properties]
  - [custom properties]

AdTracker Methods

play()

Call when the ad starts playing. AdTracker initializes in a paused state, so call this immediately if the ad auto-plays:
adTracker.play()

play(positionMs)

Call when playback position changes (e.g., seeking):
adTracker.play(positionMs = 5000)  // Resume at 5 seconds

pause()

Call when the ad is paused or buffering:
adTracker.pause()

clicked()

Call when the user clicks on the ad. This tracks a VideoAdClicked event:
adTracker.clicked()

completion()

Call when the ad is no longer being shown. This tracks a VideoAdCompletion event with engagement properties:
adTracker.completion()
⚠️ Important Once completion() is called, the AdTracker is closed and subsequent method calls will have no effect.

Custom Events

Track custom ad-related events:
// Track without additional properties
adTracker.trackWithAdProperties("AdMuted")

// Track with additional properties
adTracker.trackWithAdProperties(
    "AdQualityChanged",
    EventProperties.from(
        "from_quality" to "480p",
        "to_quality" to "720p"
    )
)

Ad Properties

Standard Ad Properties

AdTracker.AdProperties(
    title = "Ad Title",
    durationInSeconds = 30,
    isMuted = false,
    campaignId = "campaign_abc123",
    creativeId = "creative_xyz789"
)

Property Reference

PropertyTypeDescription
titleStringAd title or name
durationInSecondsIntAd duration in seconds
isMutedBooleanWhether ad started muted
campaignIdStringAd campaign identifier
creativeIdStringAd creative identifier
All properties are passed to every event tracked through the AdTracker.

Complete Example

Kotlin - Pre-Roll Ad

import com.permutive.android.MediaTracker
import com.permutive.android.AdTracker
import com.permutive.android.EventProperties

class VideoFragment : Fragment() {
    
    private val permutive by lazy {
        (requireActivity().application as MyApplication).permutive
    }
    
    private lateinit var videoTracker: MediaTracker
    private lateinit var adTracker: AdTracker
    private lateinit var exoPlayer: ExoPlayer
    
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Create video tracker
        videoTracker = permutive.trackVideoView(
            durationMilliseconds = 180000,  // 3 minute video
            videoProperties = MediaTracker.VideoProperties(
                title = "Main Video Content"
            )
        )
        
        // Create ad tracker for pre-roll ad
        adTracker = videoTracker.trackAdView(
            durationMs = 15000,  // 15 second ad
            adProperties = AdTracker.AdProperties(
                title = "Pre-Roll Advertisement",
                durationInSeconds = 15,
                isMuted = false,
                campaignId = "preroll_camp_123",
                creativeId = "creative_456"
            )
        )
        
        setupAdPlayer()
        
        return inflater.inflate(R.layout.fragment_video, container, false)
    }
    
    private fun setupAdPlayer() {
        exoPlayer = ExoPlayer.Builder(requireContext()).build()
        
        // Load ad content
        val adUri = Uri.parse("https://example.com/ad.mp4")
        val adMediaItem = MediaItem.fromUri(adUri)
        exoPlayer.setMediaItem(adMediaItem)
        exoPlayer.prepare()
        
        // Track ad playback
        exoPlayer.addListener(object : Player.Listener {
            override fun onIsPlayingChanged(isPlaying: Boolean) {
                if (isPlaying) {
                    adTracker.play()
                } else {
                    adTracker.pause()
                }
            }
            
            override fun onPlaybackStateChanged(state: Int) {
                when (state) {
                    Player.STATE_ENDED -> {
                        // Ad finished
                        adTracker.completion()
                        // Now load main video content
                        loadMainVideoContent()
                    }
                    Player.STATE_BUFFERING -> {
                        adTracker.pause()
                    }
                }
            }
        })
        
        // Track ad clicks
        playerView.setOnClickListener {
            adTracker.clicked()
            openAdLandingPage()
        }
        
        // Start ad playback
        exoPlayer.play()
    }
    
    private fun loadMainVideoContent() {
        // Load main video
        val videoUri = Uri.parse("https://example.com/video.mp4")
        val videoMediaItem = MediaItem.fromUri(videoUri)
        exoPlayer.setMediaItem(videoMediaItem)
        exoPlayer.prepare()
        exoPlayer.play()
        
        // Now track main video with videoTracker
        videoTracker.play()
    }
    
    private fun openAdLandingPage() {
        val intent = Intent(Intent.ACTION_VIEW, Uri.parse("https://advertiser.com/landing"))
        startActivity(intent)
    }
    
    override fun onDestroy() {
        super.onDestroy()
        videoTracker.stop()
        exoPlayer.release()
    }
}

Java - Pre-Roll Ad

import com.permutive.android.MediaTracker;
import com.permutive.android.AdTracker;

public class VideoFragment extends Fragment {
    
    private MediaTracker videoTracker;
    private AdTracker adTracker;
    private ExoPlayer exoPlayer;
    
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        Permutive permutive = ((MyApplication) requireActivity().getApplication()).getPermutive();
        
        // Create video tracker
        MediaTracker.VideoProperties videoProps = new MediaTracker.VideoProperties.Builder()
            .title("Main Video Content")
            .build();
        
        videoTracker = permutive.trackVideoView(
            180000,  // 3 minutes
            videoProps,
            null,
            null
        );
        
        // Create ad tracker
        AdTracker.AdProperties adProps = new AdTracker.AdProperties(
            "Pre-Roll Advertisement",
            15,     // 15 seconds
            false,  // not muted
            "preroll_camp_123",
            "creative_456"
        );
        
        adTracker = videoTracker.trackAdView(15000, adProps, null);
        
        setupAdPlayer();
        
        return inflater.inflate(R.layout.fragment_video, container, null);
    }
    
    private void setupAdPlayer() {
        exoPlayer = new ExoPlayer.Builder(requireContext()).build();
        
        // Load ad
        Uri adUri = Uri.parse("https://example.com/ad.mp4");
        MediaItem adMediaItem = MediaItem.fromUri(adUri);
        exoPlayer.setMediaItem(adMediaItem);
        exoPlayer.prepare();
        
        // Track ad playback
        exoPlayer.addListener(new Player.Listener() {
            @Override
            public void onIsPlayingChanged(boolean isPlaying) {
                if (isPlaying) {
                    adTracker.play();
                } else {
                    adTracker.pause();
                }
            }
            
            @Override
            public void onPlaybackStateChanged(int state) {
                if (state == Player.STATE_ENDED) {
                    adTracker.completion();
                    loadMainVideoContent();
                } else if (state == Player.STATE_BUFFERING) {
                    adTracker.pause();
                }
            }
        });
        
        // Track clicks
        playerView.setOnClickListener(v -> {
            adTracker.clicked();
            openAdLandingPage();
        });
        
        exoPlayer.play();
    }
    
    private void loadMainVideoContent() {
        Uri videoUri = Uri.parse("https://example.com/video.mp4");
        MediaItem videoMediaItem = MediaItem.fromUri(videoUri);
        exoPlayer.setMediaItem(videoMediaItem);
        exoPlayer.prepare();
        exoPlayer.play();
        
        videoTracker.play();
    }
    
    private void openAdLandingPage() {
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://advertiser.com/landing"));
        startActivity(intent);
    }
    
    @Override
    public void onDestroy() {
        super.onDestroy();
        videoTracker.stop();
        exoPlayer.release();
    }
}

Use Cases

Ad shown before video content starts:
// Create video tracker first
val videoTracker = permutive.trackVideoView(...)

// Create ad tracker from video tracker
val preRollAd = videoTracker.trackAdView(
    durationMs = 15000,
    adProperties = AdTracker.AdProperties(
        title = "Pre-Roll Ad",
        durationInSeconds = 15,
        campaignId = "preroll_123"
    )
)

// Play ad
preRollAd.play()
// ... ad plays ...
preRollAd.completion()

// Then play main video
videoTracker.play()

Tracking Ad Interactions

Click Tracking

// User clicks ad
adClickArea.setOnClickListener {
    adTracker.clicked()  // Tracks VideoAdClicked event
    openAdvertiserUrl()
}

Skip Tracking

// User skips ad
skipButton.setOnClickListener {
    adTracker.trackWithAdProperties(
        "VideoAdSkipped",
        EventProperties.from(
            "time_watched_seconds" to getWatchedTime()
        )
    )
    adTracker.completion()
    loadMainContent()
}

Error Tracking

// Ad fails to load
adPlayer.setErrorListener { error ->
    adTracker.trackWithAdProperties(
        "VideoAdError",
        EventProperties.from(
            "error_code" to error.code,
            "error_message" to error.message
        )
    )
    adTracker.completion()
}

Troubleshooting

Problem: VideoAdCompletion events not appearing.Cause: completion() not called.Solution: Always call completion() when ad finishes:
// When ad ends
adTracker.completion()
Problem: Ad events have same View ID as video.Cause: This is expected when creating AdTracker from MediaTracker.This is correct behavior - it links ad to video content. For independent tracking, use standalone AdTracker:
// Standalone (separate View ID)
val adTracker = permutive.trackVideoAdView(...)
Problem: Engaged time is wrong.Causes:
  1. Not calling play() when ad starts
  2. Not calling pause() for buffering
Solution: Track all state changes:
player.addListener(object : Player.Listener {
    override fun onIsPlayingChanged(isPlaying: Boolean) {
        if (isPlaying) adTracker.play() else adTracker.pause()
    }
})
Problem: Multiple VideoAdCompletion events tracked.Cause: Calling completion() multiple times.Solution: Call completion() only once. After calling it, AdTracker is closed.See Common Errors for more troubleshooting.

Best Practices

  • Always call completion() when ad finishes
  • Track play/pause accurately
  • Use clicked() for click tracking
  • Create from MediaTracker for in-content ads
  • Provide campaign and creative IDs
  • Call play() immediately if ad auto-plays


API Reference

For complete API documentation, see the Javadocs.

AdTracker Interface

  • play() - Start/resume ad playback
  • play(positionMs: Long) - Start at specific position
  • pause() - Pause ad playback
  • clicked() - Track ad click (sends VideoAdClicked event)
  • completion() - Complete ad tracking (sends VideoAdCompletion event)
  • trackWithAdProperties(eventName: String) - Track custom event with ad properties
  • trackWithAdProperties(eventName: String, customProperties: EventProperties) - Track custom event with ad and custom properties

Creating AdTracker

  • MediaTracker.trackAdView(durationMs, adProperties, customEventProperties) - Create from MediaTracker (shares View ID)
  • Permutive.trackVideoAdView(durationMs, adProperties, customEventProperties) - Create standalone AdTracker

AdProperties

  • AdTracker.AdProperties(title, durationInSeconds, isMuted, campaignId, creativeId) - Ad property container