> ## Documentation Index
> Fetch the complete documentation index at: https://docs.permutive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Video Ad Tracking

> Track video advertisements, clicks, and completions

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

<CardGroup cols={4}>
  <Card title="Creating AdTracker" href="#creating-an-adtracker" icon="plus" />

  <Card title="Ad Methods" href="#adtracker-methods" icon="code" />

  <Card title="Use Cases" href="#use-cases" icon="layer-group" />

  <Card title="Interactions" href="#tracking-ad-interactions" icon="hand-pointer" />
</CardGroup>

## 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:

### 1. From MediaTracker (Recommended)

Use this when the ad is related to video content currently being viewed. The AdTracker will share the same View ID with the MediaTracker:

```kotlin theme={"dark"}
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:

```kotlin theme={"dark"}
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:

```kotlin theme={"dark"}
adTracker.play()
```

### play(positionMs)

Call when playback position changes (e.g., seeking):

```kotlin theme={"dark"}
adTracker.play(positionMs = 5000)  // Resume at 5 seconds
```

### pause()

Call when the ad is paused or buffering:

```kotlin theme={"dark"}
adTracker.pause()
```

### clicked()

Call when the user clicks on the ad. This tracks a **VideoAdClicked** event:

```kotlin theme={"dark"}
adTracker.clicked()
```

### completion()

Call when the ad is no longer being shown. This tracks a **VideoAdCompletion** event with engagement properties:

```kotlin theme={"dark"}
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:

```kotlin theme={"dark"}
// 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

```kotlin theme={"dark"}
AdTracker.AdProperties(
    title = "Ad Title",
    durationInSeconds = 30,
    isMuted = false,
    campaignId = "campaign_abc123",
    creativeId = "creative_xyz789"
)
```

### Property Reference

| Property              | Type    | Description              |
| --------------------- | ------- | ------------------------ |
| **title**             | String  | Ad title or name         |
| **durationInSeconds** | Int     | Ad duration in seconds   |
| **isMuted**           | Boolean | Whether ad started muted |
| **campaignId**        | String  | Ad campaign identifier   |
| **creativeId**        | String  | Ad creative identifier   |

All properties are passed to every event tracked through the AdTracker.

***

## Complete Example

### Kotlin - Pre-Roll Ad

```kotlin theme={"dark"}
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

```java theme={"dark"}
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

<Tabs>
  <Tab title="Pre-Roll Ad">
    Ad shown **before** video content starts:

    ```kotlin theme={"dark"}
    // 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()
    ```
  </Tab>

  <Tab title="Mid-Roll Ad">
    Ad shown **during** video content:

    ```kotlin theme={"dark"}
    // Main video playing
    videoTracker.pause()

    // Create mid-roll ad
    val midRollAd = videoTracker.trackAdView(
        durationMs = 30000,
        adProperties = AdTracker.AdProperties(
            title = "Mid-Roll Ad",
            durationInSeconds = 30,
            campaignId = "midroll_456"
        )
    )

    midRollAd.play()
    // ... ad plays ...
    midRollAd.completion()

    // Resume main video
    videoTracker.play()
    ```
  </Tab>

  <Tab title="Standalone Ad">
    Ad **not associated** with video content:

    ```kotlin theme={"dark"}
    // Ad not associated with video content
    val standaloneAd = permutive.trackVideoAdView(
        durationMs = 30000,
        adProperties = AdTracker.AdProperties(
            title = "Standalone Video Ad",
            durationInSeconds = 30,
            campaignId = "standalone_789"
        )
    )

    standaloneAd.play()
    // ... ad plays ...
    standaloneAd.completion()
    ```
  </Tab>

  <Tab title="Ad Pod">
    **Multiple ads** played in sequence:

    ```kotlin theme={"dark"}
    val ads = listOf(
        AdInfo("Ad 1", 15000, "camp_1", "creative_1"),
        AdInfo("Ad 2", 15000, "camp_2", "creative_2"),
        AdInfo("Ad 3", 30000, "camp_3", "creative_3")
    )

    ads.forEach { adInfo ->
        val adTracker = videoTracker.trackAdView(
            durationMs = adInfo.duration,
            adProperties = AdTracker.AdProperties(
                title = adInfo.title,
                durationInSeconds = adInfo.duration / 1000,
                campaignId = adInfo.campaignId,
                creativeId = adInfo.creativeId
            )
        )

        // Play ad
        adTracker.play()
        // ... wait for completion ...
        adTracker.completion()
    }
    ```
  </Tab>
</Tabs>

***

## Tracking Ad Interactions

### Click Tracking

```kotlin theme={"dark"}
// User clicks ad
adClickArea.setOnClickListener {
    adTracker.clicked()  // Tracks VideoAdClicked event
    openAdvertiserUrl()
}
```

### Skip Tracking

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

### Error Tracking

```kotlin theme={"dark"}
// 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

<AccordionGroup>
  <Accordion title="VideoAdCompletion Not Tracked">
    **Problem:** VideoAdCompletion events not appearing.

    **Cause:** `completion()` not called.

    **Solution:** Always call `completion()` when ad finishes:

    ```kotlin theme={"dark"}
    // When ad ends
    adTracker.completion()
    ```
  </Accordion>

  <Accordion title="Ad and Video Using Same ID">
    **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:

    ```kotlin theme={"dark"}
    // Standalone (separate View ID)
    val adTracker = permutive.trackVideoAdView(...)
    ```
  </Accordion>

  <Accordion title="Engagement Time Incorrect">
    **Problem:** Engaged time is wrong.

    **Causes:**

    1. Not calling `play()` when ad starts
    2. Not calling `pause()` for buffering

    **Solution:** Track all state changes:

    ```kotlin theme={"dark"}
    player.addListener(object : Player.Listener {
        override fun onIsPlayingChanged(isPlaying: Boolean) {
            if (isPlaying) adTracker.play() else adTracker.pause()
        }
    })
    ```
  </Accordion>

  <Accordion title="Multiple Completion Events">
    **Problem:** Multiple VideoAdCompletion events tracked.

    **Cause:** Calling `completion()` multiple times.

    **Solution:** Call `completion()` only once. After calling it, AdTracker is closed.

    See [Common Errors](/sdks/mobile/android/troubleshooting/common-errors) for more troubleshooting.
  </Accordion>
</AccordionGroup>

***

## Best Practices

<Tabs>
  <Tab title="Do">
    * 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
  </Tab>

  <Tab title="Don't">
    * Forget to call `completion()`
    * Call methods after `completion()`
    * Skip play/pause lifecycle events
    * Use standalone tracker for in-content ads
  </Tab>
</Tabs>

***

## Related Documentation

<CardGroup cols={2}>
  <Card title="Video Tracking" icon="video" href="/sdks/mobile/android/features/video-tracking">
    Track video content
  </Card>

  <Card title="Event Properties" icon="list" href="/sdks/mobile/android/core-concepts/event-properties">
    Custom properties
  </Card>

  <Card title="Page Tracking" icon="file" href="/sdks/mobile/android/features/page-tracking">
    Track pages
  </Card>

  <Card title="Issues" icon="wrench" href="/sdks/mobile/android/troubleshooting/common-errors">
    Solve common issues
  </Card>
</CardGroup>

***

## API Reference

For complete API documentation, see the [Javadocs](https://sdk-docs.permutive.com/index.html).

### 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
