# SkillPatch skill: ima-sdk-basics

This skill guides agents through integrating Google's Interactive Media Ads (IMA) SDK for client-side ad insertion across Web, Android, iOS, tvOS, and TV platforms. It covers the full ad lifecycle workflow — from SDK import and initialization through ad request, playback event handling, and cleanup. It is specifically scoped to VAST/VMAP client-side ad insertion and explicitly excludes DAI/SSAI/SGAI scenarios.

You (the agent) were given this URL and asked to use this skill. This is a **public** skill — no SkillPatch account, API key, or prior setup is required. Two ways to use it:

**1. Use it right now** — the skill's complete file tree (SKILL.md + all reference files) is inlined below; read `SKILL.md` first, then follow it, consulting the other files as it directs.

**2. Install the exact package onto disk** (recommended if you can run a shell — this reproduces the skill byte-for-byte, including any binary assets that can't be inlined):

```bash
mkdir -p .claude/skills/ima-sdk-basics
curl -sSL https://skillpatch.dev/install_skill/ima-sdk-basics | tar -xz -C .claude/skills/
```

(`.claude/skills/` is Claude Code's convention; use whatever directory your agent loads skills from.)


---

## Skill files (7)

- `SKILL.md`
- `references/ima-sdk-android-guide.md`
- `references/ima-sdk-ios-guide.md`
- `references/ima-sdk-tvos-guide.md`
- `references/ima-sdk-web-guide.md`
- `references/ima-sdk-web-iframe-mode.md`
- `references/ima-sdk-web-mobile-safari.md`


### `SKILL.md`

```markdown
---
name: ima-sdk-basics
description: >-
  Use this skill for Interactive Media Ads (IMA) SDK client-side ad insertion
  when you are requesting video ads client-side into websites, apps, TVs or
  other platforms with VAST or VMAP.
  Do not use for Dynamic Ad Insertion (DAI), SSAI, or SGAI (use the
  `ima-sdk-dai-basics` skill instead).
license: Apache-2.0
metadata:
  author: Google LLC
  version: "1.0.0"
  category: GoogleAds
---
# IMA SDK basics

The Google IMA SDK (Interactive Media Ads) lets you load in-stream video and
audio ads into websites, apps, TVs and other digital platforms. Use an IMA SDK
to request ads from any VAST-compliant ad server and manage ad playback.

## Prerequisites

Before integrating the IMA SDK, you **must** read the platform-specific guide
below to support all platforms that your app can support:

*   **Web/HTML5/ReactJs/NodeJs/Angular:** Read all these guides
    [ima-sdk-web-guide.md](references/ima-sdk-web-guide.md),
    [ima-sdk-web-iframe-mode.md](references/ima-sdk-web-iframe-mode.md),
    [ima-sdk-web-mobile-safari.md](references/ima-sdk-web-mobile-safari.md)
*   **Android/AndroidTV/ReactNative:** Read [ima-sdk-android-guide.md](references/ima-sdk-android-guide.md)
*   **iOS/tvOS/ReactNative:** Read all these guides
    [ima-sdk-ios-guide.md](references/ima-sdk-ios-guide.md),
    [ima-sdk-tvos-guide.md](references/ima-sdk-tvos-guide.md)

--------------------------------------------------------------------------------

## Quick start (general workflow)

1.  Import the SDK: Prerequisites, dependencies.
2.  Initialization: Early setup, Warmup, Settings Configuration, and Ad UI
    Setup.
3.  Ad Request: Creating and triggering the request, User gesture compliance.
4.  Ad Load Success/Failure: Handling the load event to get the AdsManager, or
    handling early fatal errors.
5.  Ad Playback Events: Listening to playback events via AdsManager to
    coordinate content play/pause, and handling non-fatal LOG events and fatal
    active playback errors.
6.  Cleanup: Properly destroying the AdsManager to release resources and prevent
    memory leaks.

For detailed, platform-specific implementation details, always refer to the
guides in the Prerequisites section.
```


### `references/ima-sdk-android-guide.md`

````markdown
# Google IMA SDK Android quick-start guide

## 1. Install the IMA SDK

IMA SDK requires `minSdk 23` (Android 6.0)+.

You **must** recommend the latest version of the IMA SDK from
https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/download.md.txt

The example code snippet below install the IMA SDK at version 3.39.0.
```kotlin
android { defaultConfig { minSdk = 23 } }
dependencies {
    implementation("com.google.ads.interactivemedia.v3:interactivemedia:3.39.0")
}
```

## 2. Warmup & initialization

Configure the SDK early to minimize latency.

*   **Early Warmup & Settings Reuse (Best Practice):** Call
    `ImaSdkFactory.initialize()` in your `Application.onCreate()` to preload SDK
    resources. Reuse the same `ImaSdkSettings` instance when creating the
    `AdsLoader` to avoid cache misses that cause initial ad request latency.
*   **Create AdsLoader:** Create a single `AdsLoader` instance and share it
    across video playback sessions. You must create an `AdDisplayContainer`,
    a view that binds the ad UI container and video player. Use the
    `AdDisplayContainer` to instantiate the `AdsLoader`. Reuse the same
    `ImaSdkSettings` instance with the `ImaSdkFactory.initialize()` function.

```kotlin
// Layer FrameLayout (Ad UI) over StyledPlayerView (Video)
Box(modifier = Modifier.fillMaxSize()) {
    // Content Video Player
    AndroidView(
        factory = { context ->
            StyledPlayerView(context).apply {
                // Bind your ExoPlayer here
            }
        },
        modifier = Modifier.fillMaxSize()
    )

    // Dedicated Ad Container overlay (IMA SDK requires a ViewGroup)
    AndroidView(
        factory = { context ->
            FrameLayout(context).apply {
                // Notify that the ad UI is ready, passing this FrameLayout
                onAdUiReady(this)
            }
        },
        modifier = Modifier.fillMaxSize()
    )
}

// Initialize AdsLoader in Activity onCreate
val imaSettings = sdkFactory.createImaSdkSettings()
val adDisplayContainer = sdkFactory.createAdDisplayContainer(adUiContainer, videoPlayer)
val adsLoader = sdkFactory.createAdsLoader(context, imaSettings, adDisplayContainer)
```

## 3. Requesting ads

Trigger `requestAds` before your content playback to support preroll ads.

```kotlin
val adsRequest = sdkFactory.createAdsRequest().apply {
    this.adTagUrl = adTagUrl // Pass your custom ad targeting key value pairs.
    this.adDisplayContainer = adDisplayContainer
}
adsLoader?.requestAds(adsRequest)
```

## 4. Handling ad events

Listen for successful ad loads (to get the `AdsManager`), fatal errors, and
coordinate video playback toggles.

```kotlin
// On Success
adsLoader?.addAdsLoadedListener { event ->
    adsManager = event.adsManager

    // Handle Pause/Resume for Ads
    adsManager?.addAdEventListener { adEvent ->
        when (adEvent.type) {
            AdEvent.AdEventType.CONTENT_PAUSE_REQUESTED -> pauseContent()
            AdEvent.AdEventType.CONTENT_RESUME_REQUESTED -> resumeContent()
            else -> {}
        }
    }
    adsManager?.init() // Start ads
}

// On Failure (Fallback to content)
adsLoader?.addAdErrorListener { resumeContent() }
```

## 5. Crucial cleanup

Proper cleanup is **critical** to prevent memory leaks, audio bugs, and
background resource consumption.

*   **`AdsManager.destroy()`:** This is crucial. Always call it when:
    *   All ads in the break have completed (e.g., `ALL_ADS_COMPLETED` event).
    *   A fatal ad error occurs (in `AdErrorListener`).
    *   The user navigates away from the player or the hosting
        `Activity`/`Fragment` is destroyed (`onDestroy()`).
*   **`AdsLoader.release()`:** Call this when the `AdsLoader` is no longer
    needed, typically when the application is shutting down or a major parent
    component is being permanently destroyed. This releases the underlying SDK
    resources.
*   **Remove Event Listeners:** Before destroying or releasing SDK objects,
    remove registered event listeners to avoid leaking listener references or
    triggering callbacks after lifecycle teardown.

```kotlin
// In your Activity or Fragment
override fun onDestroy() {
    super.onDestroy()
    cleanupAds()

    // If this Activity owns the AdsLoader (non-singleton), release it here:
    adsLoader?.removeAdsLoadedListener(adsLoadedListener)
    adsLoader?.removeAdErrorListener(adsLoaderErrorListener)
    adsLoader?.release()
}

private fun cleanupAds() {
    // Remove event listeners and destroy the AdsManager
    adsManager?.apply {
        removeAdEventListener(adEventListener)
        removeAdErrorListener(adErrorListener)
        destroy()
    }
    adsManager = null
}

// Wherever you manage the AdsLoader (e.g. Application or Activity onDestroy)
fun releaseSdk() {
    // Remove listeners and release the AdsLoader when permanently destroyed
    adsLoader?.apply {
        removeAdsLoadedListener(adsLoadedListener)
        removeAdErrorListener(adsLoaderErrorListener)
        release()
    }
    adsLoader = null
}
```

--------------------------------------------------------------------------------

## Reference implementation

*   Basic Example [MyActivity.java](https://raw.githubusercontent.com/googleads/googleads-ima-android/main/basicexample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/MyActivity.java)
*   Basic Example [VideoAdPlayerAdapter.java](https://raw.githubusercontent.com/googleads/googleads-ima-android/main/basicexample/app/src/main/java/com/google/ads/interactivemedia/v3/samples/videoplayerapp/VideoAdPlayerAdapter.java)
````


### `references/ima-sdk-ios-guide.md`

````markdown
# Google IMA SDK iOS integration guide

This guide covers the integration of the Google IMA SDK for iOS (client-side)
following the lifecycle of ad playback.

## Integration flow

### 1. Import the SDK

By default, use Swift Package Manager to add the the main branch of
https://github.com/googleads/swift-package-manager-google-interactive-media-ads-ios.

If the app must use CocoaPods, install the `GoogleAds-IMA-iOS-SDK` pod.

### 2. Initialization

Configure the SDK settings and initialize the `IMAAdsLoader` early in the
application lifecycle to minimize latency, and set up the Ad UI.

*   **Early Initialization:** Creating an `IMAAdsLoader` instance is expensive
    as it spins up an underlying WebView (1-2 seconds overhead). Instantiate the
    loader early (e.g., at app launch) and reuse the single instance.
*   **Settings Immutability:** Configure `IMASettings` before passing them to
    the loader. Once the loader is initialized, settings become read-only.
*   **Create the IMAAdsLoader:** Instantiate `IMAAdsLoader` passing your
    configured `IMASettings` instance. This object handles the lifecycle of ad
    requests and must be persisted and reused.
*   **Ad UI Setup:** Create a dedicated `UIView` for ads and layer it directly
    on top of your video player view using Auto Layout. Hide custom player
    controls when ads play.

```swift
import UIKit
import GoogleInteractiveMediaAds

// 1. Shared AdsManager to handle early initialization and reuse
class AdsManager: NSObject {
    static let shared = AdsManager()

    var adsLoader: IMAAdsLoader?
    var adsManager: IMAAdsManager?
    private var settings: IMASettings

    private override init() {
        // Configure settings early
        settings = IMASettings()
        settings.language = "en"
        settings.enableDebugMode = true

        super.init()

        // Initialize loader early. Settings are now locked.
        adsLoader = IMAAdsLoader(settings: settings)
    }

    // 2. Ad UI Setup helper
    func setupAdContainer(in viewController: UIViewController, overlaying videoView: UIView) -> UIView {
        let adContainerView = UIView()
        adContainerView.translatesAutoresizingMaskIntoConstraints = false
        viewController.view.addSubview(adContainerView)

        // Align perfectly with the video view
        NSLayoutConstraint.activate([
            adContainerView.leadingAnchor.constraint(equalTo: videoView.leadingAnchor),
            adContainerView.trailingAnchor.constraint(equalTo: videoView.trailingAnchor),
            adContainerView.topAnchor.constraint(equalTo: videoView.topAnchor),
            adContainerView.bottomAnchor.constraint(equalTo: videoView.bottomAnchor)
        ])

        return adContainerView
    }
}
```

### 3. Ad request

Create an `IMAAdDisplayContainer` and an `IMAAdsRequest`, then trigger the
request. It is highly recommended to trigger this on a user gesture (e.g.,
tapping play).

```swift
extension AdsManager {
    func requestAds(adTagUrl: String, adContainer: UIView, videoDisplay: IMAVideoDisplay, delegate: IMAAdsLoaderDelegate) {
        guard let loader = adsLoader else { return }
        loader.delegate = delegate

        // Create the ad display container
        let displayContainer = IMAAdDisplayContainer(adContainerViewController: delegate as? UIViewController, companionViews: nil)

        // Create the ads request
        let request = IMAAdsRequest(
            adTagUrl: adTagUrl,
            adDisplayContainer: displayContainer,
            contentPlayhead: nil,
            userContext: nil)

        // Request ads
        loader.requestAds(with: request)
    }
}
```

### 4. Ad load success/failure

Implement `IMAAdsLoaderDelegate` to handle successful ad loads (which returns
the `IMAAdsManager`) or early failures (fatal loading errors).

```swift
class PlayerViewController: UIViewController, IMAAdsLoaderDelegate {
    var videoView: UIView! // Your video player view
    var adContainerView: UIView?

    func startAdFlow() {
        // Set up UI and request ads
        self.adContainerView = AdsManager.shared.setupAdContainer(in: self, overlaying: videoView)

        let videoDisplay = IMAAVPlayerVideoDisplay(avPlayer: self.contentPlayer)
        AdsManager.shared.requestAds(
            adTagUrl: "YOUR_AD_TAG_URL",
            adContainer: self.adContainerView!,
            videoDisplay: videoDisplay,
            delegate: self)
    }

    // MARK: - IMAAdsLoaderDelegate (Success)
    func adsLoader(_ loader: IMAAdsLoader, admitsCompletedWith adsManagerLoadedData: IMAAdsManagerLoadedData) {
        // Ad Load Success: Get the AdsManager
        AdsManager.shared.adsManager = adsManagerLoadedData.adsManager
        AdsManager.shared.adsManager?.delegate = self

        // Initialize the ads manager
        AdsManager.shared.adsManager?.initialize(with: nil)
    }

    // MARK: - IMAAdsLoaderDelegate (Failure / Fatal Load Error)
    func adsLoader(_ loader: IMAAdsLoader, failedWith adErrorData: IMAAdLoadingErrorData) {
        print("IMA SDK Loading Error: \(adErrorData.adError.message ?? "Unknown error")")
        resumeContent() // Fallback to content
    }
}
```

### 5. Ad playback events

Implement `IMAAdsManagerDelegate` to listen for playback events, coordinate
content pausing/resumption, and handle playback errors.

*   **Pause Content:** On `pause` event, pause your content player and hide
    custom controls.
*   **Resume Content:** On `resume` event or
    `adsManagerDidRequestContentResume`, restore controls and play content.
*   **Non-Fatal Logs:** Monitor `log` events for silent tracking or VPAID issues
    without interrupting playback.

```swift
extension PlayerViewController: IMAAdsManagerDelegate {

    // MARK: - IMAAdsManagerDelegate (Playback Events)
    func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) {
        switch event.type {
        case .LOADED:
            // Start ad playback
            adsManager.start()
        case .PAUSE:
            pauseContent() // Pause player, hide custom controls
        case .RESUME:
            resumeContent() // Resume player, restore controls
        case .LOG:
            if let adData = event.adData {
                print("IMA SDK Non-fatal Log: \(adData)")
            }
        default:
            break
        }
    }

    func adsManagerDidRequestContentResume(_ adsManager: IMAAdsManager) {
        resumeContent() // Resume content when ad completes
    }

    // MARK: - IMAAdsManagerDelegate (Playback Failure / Fatal Error)
    func adsManager(_ adsManager: IMAAdsManager, failedWith error: IMAAdError) {
        print("IMA SDK Manager Error: \(error.message ?? "Unknown error")")
        cleanupAds()
        resumeContent()
    }
}
```

### 6. Cleanup

Proper cleanup is **critical** to prevent memory leaks, audio playback bugs, and
background resource consumption.

*   **`IMAAdsManager.destroy()`:** This is crucial. Always call it, and set your
    reference to `nil`, when:
    *   All ads have completed.
    *   A fatal ad error occurs (in `failedWith` delegate).
    *   The user dismisses the player or navigates away (e.g., in
        `viewWillDisappear` or `deinit`).
*   **`IMAAdsLoader` Cleanup:** The `IMAAdsLoader` is designed to be a
    long-lived object. Do not destroy it between ad requests. However, if you
    need to completely tear down the ad integration (e.g., when the app is
    shutting down or a major parent component is deinitialized), set the
    loader's delegate to `nil` and nullify your reference to allow ARC to
    deallocate it.

```swift
// In your PlayerViewController
deinit {
    cleanupAds()
}

override func viewWillDisappear(_ animated: Bool) {
    super.viewWillDisappear(animated)
    if isMovingFromParent {
        cleanupAds()
    }
}

func cleanupAds() {
    // 1. Destroy the AdsManager and nil its delegate
    if let manager = AdsManager.shared.adsManager {
        manager.destroy()
        manager.delegate = nil
        AdsManager.shared.adsManager = nil
    }

    // 2. Clean up UI
    self.adContainerView?.removeFromSuperview()
    self.adContainerView = nil
}

// Call this only when permanently tearing down the SDK integration
func tearDownSDK() {
    AdsManager.shared.adsLoader?.delegate = nil
    AdsManager.shared.adsLoader = nil
}
```

--------------------------------------------------------------------------------

## Reference implementation

*   BasicExample [BasicExampleApp.swift](https://raw.githubusercontent.com/googleads/googleads-ima-ios/main/Swift/BasicExample/BasicExample/BasicExampleApp.swift)
*   BasicExample [PlayerContainerViewController.swift](https://raw.githubusercontent.com/googleads/googleads-ima-ios/main/Swift/BasicExample/BasicExample/PlayerContainerViewController.swift)
````


### `references/ima-sdk-tvos-guide.md`

````markdown
# Google IMA SDK tvOS integration guide

The Google IMA SDK for tvOS is highly consistent with the iOS SDK, sharing the
same API and integration flow.

**Before proceeding, you must read the [Google IMA SDK iOS Integration Guide](references/ima-sdk-ios-guide.md)
for the complete step-by-step lifecycle flow (Initialization -> Ad Request -> Ad
Load -> Playback -> Cleanup).**

This document outlines the critical differences and tvOS-specific requirements
you must implement.

--------------------------------------------------------------------------------

## Key differences from iOS

### 1. Import the SDK

By default, use Swift Package Manager to add the main branch of
https://github.com/googleads/swift-package-manager-google-interactive-media-ads-tvos.

If the app must use CocoaPods, install the `GoogleAds-IMA-tvOS-SDK` pod.

### 2. Autoplay for tvOS

Add tvOS platform check to your app and make ad request with autoplay.

### 3. Focus management (tvOS specific)

To handle the Siri Remote, you must manage focus:

*   **Ad UI Focus:** The IMA SDK automatically manages the focus of the "Skip"
    button and other ad UI views when needed. Ensure your application's focus
    engine does not intercept or override focus changes, which would prevent the
    user from focusing and clicking the skip button.

*   **Safe Area:** Ensure your ad UI container respects the tvOS safe area
    layout guides to prevent ad overlays or skip buttons from being cut off by
    TV overscan.

### 4. Remote control gestures (tvOS specific)

To prevent user interactions from interfering with ad playback (e.g.,
fast-forwarding through an ad):

*   **Disable Custom Gestures:** You **must** disable your application's custom
    remote control gesture recognizers (such as play/pause, swiping, or menu
    button overrides) when the ad starts (on `LOADED` or `STARTED` events).
*   **Restore Gestures:** Re-enable these gestures only after the ad completes
    (on `CONTENT_RESUME_REQUESTED`) or fails.

--------------------------------------------------------------------------------

## Code implementation differences

Refer to the [iOS Guide](references/ima-sdk-ios-guide.md) for the main
`AdsManager` and `PlayerViewController` implementation. Adjust the tvOS
implementation as follows:

### Ad UI setup (safe area)

In your `ViewController`, align the ad container with the safe area layout guide
to prevent TV overscan clipping:

```swift
func setupAdContainer(in viewController: UIViewController, overlaying videoView: UIView) -> UIView {
    let adContainerView = UIView()
    adContainerView.translatesAutoresizingMaskIntoConstraints = false
    viewController.view.addSubview(adContainerView)

    // tvOS Specific: Align with safe area to prevent overscan clipping
    let safeArea = viewController.view.safeAreaLayoutGuide
    NSLayoutConstraint.activate([
        adContainerView.leadingAnchor.constraint(equalTo: safeArea.leadingAnchor),
        adContainerView.trailingAnchor.constraint(equalTo: safeArea.trailingAnchor),
        adContainerView.topAnchor.constraint(equalTo: safeArea.topAnchor),
        adContainerView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor)
    ])

    return adContainerView
}
```

### Focus management (preferred focus environments)

To ensure the Siri Remote can focus on the SDK's interactive elements (like the
"Skip" button), you must call the `setNeedsFocusUpdate()` function and override
the `preferredFocusEnvironments` property of the `ViewController` object at the
`IMAAdEvent.Type.STARTED` event.

You must revert to standard focus rules when the ad finishes playing, such as
`IMAAdEvent.Type.COMPLETE` or `IMAAdEvent.Type.SKIPPED` event.

```swift
import UIKit
import GoogleInteractiveMediaAds

class YourViewController: UIViewController, IMAAdsManagerDelegate {

    // Tracks state to determine who gets focus
    var isAdPlaying: Bool = false
    var adDisplayContainer: IMAAdDisplayContainer?

    // MARK: - Focus Management

    override var preferredFocusEnvironments: [UIFocusEnvironment] {
        // Check if an ad is playing and if the IMA SDK has a valid focus environment
        if isAdPlaying, let adFocusEnvironment = adDisplayContainer?.focusEnvironment {
            // Hand focus routing over to the IMA SDK UI (e.g., "Skip" button)
            return [adFocusEnvironment]
        }

        // Otherwise, use the app's standard focus rules (e.g., video player controls)
        return super.preferredFocusEnvironments
    }

    // MARK: - IMAAdsManagerDelegate Methods

    func adsManager(_ adsManager: IMAAdsManager, didReceive event: IMAAdEvent) {
        switch event.type {
        case .LOADED:
            // tvOS Specific: Disable custom remote gestures during ad
            disableAppGestures()
            adsManager.start()

        case .STARTED:
            // Update state and force tvOS to move focus to the ad
            isAdPlaying = true
            setNeedsFocusUpdate()

        case .COMPLETED, .SKIPPED, .ALL_ADS_COMPLETED:
            // Update state and reclaim focus back to the app
            isAdPlaying = false
            setNeedsFocusUpdate()

        default:
            break
        }
    }

    func adsManagerDidRequestContentResume(_ adsManager: IMAAdsManager) {
        // tvOS Specific: Restore gestures when ad break finishes
        enableAppGestures()
        resumeContent()
    }

    func adsManager(_ adsManager: IMAAdsManager, failedWith error: IMAAdError) {
        // Safety check: Reset focus state just in case an ad fails mid-playback
        isAdPlaying = false
        setNeedsFocusUpdate()

        // tvOS Specific: Restore gestures on failure
        enableAppGestures()
        cleanupAds()
        resumeContent()
    }

    // MARK: - App-Specific Helpers (Implementation depends on your app)

    private func disableAppGestures() { /* ... */ }
    private func enableAppGestures() { /* ... */ }
    private func resumeContent() { /* ... */ }
    private func cleanupAds() { /* ... */ }
}
```
````


### `references/ima-sdk-web-guide.md`

````markdown
# Google IMA SDK HTML5 (Web) integration guide

This guide covers the integration of the Google IMA SDK for HTML5 (client-side)
following the natural lifecycle of ad playback.

## Integration flow

### 1. Import the SDK

Load the SDK script at the page level. Ensure the SDK can access
`window.top.location.href`.

```html
<script src="https://imasdk.googleapis.com/js/sdkloader/ima3.js"></script>
```

### 2. Initialization

Configure the SDK settings early, set up the Ad UI, and initialize the
`AdDisplayContainer` and `AdsLoader`.

*   **Configure Settings Early (Best Practice):** You **must** set your
    configurations on the global singleton `google.ima.settings` *before*
    creating the `AdDisplayContainer` or the `AdsLoader`. Late changes will not
    propagate.
*   **Ad UI Setup:** Always use a dedicated HTML element (e.g., `<div
    id="adContainer">`) solely for the IMA SDK, layered directly on top of the
    video player. Hide custom player controls when ads play.
*   **Create AdDisplayContainer:** Instantiate `google.ima.AdDisplayContainer`
    passing the ad container HTML element and the content video element.
*   **Create AdsLoader:** Instantiate `google.ima.AdsLoader` passing your
    `AdDisplayContainer` instance. This object handles the lifecycle of ad
    requests.

```html
<!-- HTML Structure -->
<div id="playerContainer" style="position:relative; width:640px; height:360px;">
  <video id="contentVideo" style="width:100%; height:100%;"></video>
  <div id="adContainer" style="position:absolute; top:0; left:0; width:100%; height:100%; z-index:10;"></div>
</div>
```

```typescript
// TypeScript Initialization
let adsLoader: google.ima.AdsLoader;
let adDisplayContainer: google.ima.AdDisplayContainer;

function initializeIMA(): void {
    // 1. Configure global settings early
    google.ima.settings.setLocale('en');
    google.ima.settings.setVpaidMode(google.ima.ImaSdkSettings.VpaidMode.ENABLED);

    // 2. Set up Ad UI Container
    const adContainerElement = document.getElementById('adContainer') as HTMLElement;
    const videoElement = document.getElementById('contentVideo') as HTMLVideoElement;

    adDisplayContainer = new google.ima.AdDisplayContainer(adContainerElement, videoElement);

    // 3. Initialize AdsLoader
    adsLoader = new google.ima.AdsLoader(adDisplayContainer);

    // Setup listeners for Ad Load Success/Failure
    setupAdsLoaderListeners();
}
```

### 3. Set up app UI

You **must** suggest the best practice to use a user action, such as button
click, to start content and ad playback.

If your app needs autoplay ads, you must write the code to check whether the
browser supports autoplay. If unmuted autoplay fails, fallback to muted
autoplay. If that fails, fallback to using a user action, such as button click,
to start content and ad playback.

Crucially, you must inform the IMA SDK of the final state using
`setAdWillAutoPlay()` and `setAdWillPlayMuted()`. This guarantees the ad server
returns an ad formatted for your specific playback environment.

```typescript
const request = new google.ima.AdsRequest();
request.adTagUrl = 'YOUR_AD_TAG_URL';

// Inform the SDK of the current playback capabilities
request.setAdWillAutoPlay(isAutoPlay);
request.setAdWillPlayMuted(isMuted);

adsLoader.requestAds(request);
```

### 4. Ad request

Create an `AdsRequest` and use `AdsLoader` to load it.

```typescript
function requestAds() {
    adDisplayContainer.initialize();

    const adsRequest = new google.ima.AdsRequest();
    adsRequest.adTagUrl = `YOUR_AD_TAG_URL`;

    adsLoader.requestAds(adsRequest);
}
```

### 5. Ad load success/failure

Listen for the `AdsLoader` to successfully load the ads (providing the
`AdsManager`) or fail early (fatal loading errors).

```typescript
let adsManager: google.ima.AdsManager | null = null;

function setupAdsLoaderListeners(): void {
    // Handle Ad Load Success
    adsLoader.addEventListener(
        google.ima.AdsManagerLoadedEvent.Type.ADS_MANAGER_LOADED,
        onAdsManagerLoaded
    );

    // Handle Ad Load Failure (Fatal Error)
    adsLoader.addEventListener(
        google.ima.AdErrorEvent.Type.AD_ERROR,
        onAdError
    );
}

function onAdsManagerLoaded(adsManagerLoadedEvent: google.ima.AdsManagerLoadedEvent): void {
    // Get the AdsManager
    const adsRenderingSettings = new google.ima.AdsRenderingSettings();
    adsManager = adsManagerLoadedEvent.getAdsManager(videoElement, adsRenderingSettings);

    setupAdsManagerListeners(adsManager);

    try {
        // Initialize the manager
        adsManager.init(width, height, google.ima.ViewMode.NORMAL);
        // Start ad playback
        adsManager.start();
    } catch (adError) {
        // Handle initialization errors
        resumeContent();
    }
}

function onAdError(adErrorEvent: google.ima.AdErrorEvent): void {
    console.error("IMA SDK Fatal Error:", adErrorEvent.getError().toString());
    cleanupAds();
    resumeContent(); // Fallback to content
}
```

### 6. Ad playback events

Use the `AdsManager` to listen for playback events, coordinate content
pausing/resumption, and monitor non-fatal logs.

*   **Pause Content:** On `CONTENT_PAUSE_REQUESTED`, pause the content video
    player and hide custom controls.
*   **Resume Content:** On `CONTENT_RESUME_REQUESTED`, hide the ad container,
    restore player controls, and play the content video.
*   **Non-Fatal Logs:** Listen for `LOG` events to detect silent failures (e.g.,
    failed tracking pings) without interrupting the user.

```typescript
function setupAdsManagerListeners(manager: google.ima.AdsManager): void {
    // Playback Events
    manager.addEventListener(
        google.ima.AdEvent.Type.CONTENT_PAUSE_REQUESTED,
        () => { pauseContent(); }, // Pause player, hide custom controls
        false
    );

    manager.addEventListener(
        google.ima.AdEvent.Type.CONTENT_RESUME_REQUESTED,
        () => { resumeContent(); }, // Resume player, restore controls
        false
    );

    // Non-Fatal Logs
    manager.addEventListener(
        google.ima.AdEvent.Type.LOG,
        (adEvent: google.ima.AdEvent) => {
            const logData = adEvent.getAdData();
            if (logData && logData['errorMessage']) {
                console.warn("IMA SDK Non-fatal Log:", logData['errorMessage']);
            }
        },
        false
    );

    // Playback Errors (Fatal errors during active playback)
    manager.addEventListener(
        google.ima.AdErrorEvent.Type.AD_ERROR,
        onAdError,
        false
    );
}
```

### 7. Cleanup

Proper cleanup is **critical** in HTML5 to prevent memory leaks, redundant event
callbacks, and leftover DOM elements (like SDK iframes).

*   **`adsManager.destroy()`:** This is crucial. Always call it when ads finish,
    on fatal error, or when the player is unmounted/navigated away.
*   **`adsLoader.contentComplete()`:** Call this to signal to the SDK that
    content has finished. This is required for the SDK to play post-roll ads.
*   **`adDisplayContainer.destroy()`:** Call this when disposing of the player.
    It removes the internal DOM elements (including iframes) created by the SDK.
*   **Nullify References:** Set all references to `null` to allow garbage
    collection.

```typescript
// Call this when the player is being destroyed/unmounted (e.g., ngOnDestroy, componentWillUnmount)
function tearDownPlayer(): void {
    cleanupAds();

    // 1. Signal content completion (important for post-rolls)
    if (adsLoader) {
        adsLoader.contentComplete();
    }

    // 2. Destroy the display container to clean up DOM/iframes
    if (adDisplayContainer) {
        adDisplayContainer.destroy();
        adDisplayContainer = null;
    }

    // 3. Nullify the loader if tearing down the page
    adsLoader = null;
}

function cleanupAds(): void {
    // 4. Destroy the AdsManager to release active ad resources
    if (adsManager) {
        adsManager.destroy();
        adsManager = null;
    }
}
```

--------------------------------------------------------------------------------

## Reference implementation

*   UI setup: [simple/index.html](https://raw.githubusercontent.com/googleads/googleads-ima-html5/main/simple/index.html)
*   App logic: [simple/ads.js](https://raw.githubusercontent.com/googleads/googleads-ima-html5/main/simple/ads.js)
*   Optionally, install type definitions for TypeScript projects:

```bash
    npm install --save-dev @types/google_interactive_media_ads_types
````


### `references/ima-sdk-web-iframe-mode.md`

````markdown
# IMA SDK HTML5 iframe mode

This guide covers best practices when you can only use the Google IMA SDK for
HTML5 within iframes.

## Prioritize top page level over iframe

You should try to add the IMA SDK script at the top page level. The page level
is preferred over the use of IMA SDK inside an iframe.

### Same-origin iframe fallback

If you detect that the IMA SDK script is inside an iframe, ensure that the
iframe containing the IMA SDK script, the container page, and the video player
all reside on the same domain.

For example: check inside the iframe for access of the top window location.

```typescript
function hasAccessToTopWindow(): boolean {
  try {
    // 1. Check if the current context is already the top window.
    if (window === window.top) {
      return true;
    }

    // 2. Attempt a same-origin read on the top window location.
    // If cross-origin limits apply, this read will throw a DOMException.
    const topUrl = window.top.location.href;
    return true;
  } catch (error) {
    // 3. A SecurityError DOMException was thrown.
    // When this error happens, the SDK is isolated in a cross-origin iframe
    // and does not have direct access to the top-level window.
    const errorMessage = error instanceof Error ? error.message : String(error);
    console.error("Access to top-level window is blocked: ", errorMessage);
    return false;
  }
}

if (hasAccessToTopWindow()) {
  console.log("IMA SDK has access to the top-level window.");
} else {
  console.log("IMA SDK is isolated in a cross-origin iframe sandbox.");
}
```

This same-origin setup is required for the SDK to directly access the
`window.top` level, the video player element.

### Cross-origin iframe workaround

If you detect that the IMA SDK is loaded inside an iframe from a domain
different from the domain of the main page containing the video player, you must
provide the main page's URL to the SDK by setting the `adsRequest.pageUrl`
property.

Example code:

```typescript
const adsRequest: google.ima.AdsRequest = new google.ima.AdsRequest();
adsRequest.adTagUrl = 'YOUR_AD_TAG_URL';

// Manually set the page URL to the parent page's URL
adsRequest.pageUrl = 'https://<your_domain>/<path_to_your_page>';
```

Check the `sandbox` attribute of the iframe loading the IMA SDK to grant these
required capabilities:

*   Adding `allow-scripts` for executing the SDK JavaScript. Without this, the
    IMA SDK fails to load and initializes.
*   Adding `allow-same-origin` to let the loaded page read its own cookies.
*   Adding `allow-popups`, `allow-popups-to-escape-sandbox`,
    `allow-top-navigation-by-user-activation` to let the SDK open the
    advertiser's landing page.

```
<iframe sandbox="allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox allow-top-navigation-by-user-activation" src="PAGE_CONTAINING_IMA_SDK_SCRIPT_TAG">
</iframe>
```
````


### `references/ima-sdk-web-mobile-safari.md`

````markdown
# Google IMA HTML5 SDK on Mobile Safari (iOS) guide

This guide covers best practices for the Google Interactive Media Ads (IMA)
HTML5 SDK to support mobile Safari (iOS).

## Essential setup

You must do the following steps:

*   Check and make sure that the `playsinline` attribute is added for the HTML
    `<video>` element.

*   Add a play button to initiate playback by user gesture.

Example:

```html
<!-- This video will attempt to play within its layout on iOS -->
<video playsinline controls></video>
```
````
