md2link

In-App Ad Integration Guide

DraftApr 22, 2026

In-App Ad Integration Guide

Partner-facing guide for integrating the touchpoint-keyed in-app ad system. Covers preload/show lifecycle, remote config keys, layout options, and troubleshooting.


Note: Unlike the Confluence spec which mentions native_full_2ID (2 alternating native IDs), this implementation uses a single native ID (AdUnitIdSingle). Waterfall happens between native and interstitial, not within native. PO can re-add 2ID waterfall as a v2 enhancement if measured performance requires it.

Quick Start

Step 1 — Register touchpoints in Application.onCreate

Both native and interstitial (recommended — full waterfall):

InAppAdManager.initOnce(
    InAppAdConfig.Builder()
        .register(
            tp = "home",
            native2Id = AdUnitId.AdUnitIdSingle("ca-app-pub-xxx/native"),
            inter = AdUnitId.AdUnitIdSingle("ca-app-pub-xxx/inter"),
        )
        .build()
)

Native only (no inter fallback):

InAppAdConfig.Builder()
    .register(
        tp = "home",
        native2Id = AdUnitId.AdUnitIdSingle("ca-app-pub-xxx/native"),
    )
    .build()

Interstitial only (no native):

InAppAdConfig.Builder()
    .register(
        tp = "home",
        inter = AdUnitId.AdUnitIdSingle("ca-app-pub-xxx/inter"),
    )
    .build()

At least one of native2Id or inter must be non-null; an IllegalArgumentException is thrown at build time otherwise.

Step 2 — Preload at PO-assigned point

// Call this as early as possible (e.g. after splash, before the trigger screen)
InAppAdManager.preload("home")

Step 3 — Show at trigger point with onNextAction

InAppAdManager.show(activity, "home") {
    // Runs after ad dismissed (or skipped) — navigate forward here
    startActivity(Intent(this, HomeActivity::class.java))
}

onNextAction is always called exactly once, whether the ad shows, is skipped due to purchase/remote gate, or fails to load.


Remote Config Reference

All settings for a touchpoint are consolidated into one JSON key per touchpoint.

Key Type Default
inapp_ad_<xxx> JSON string "" (all fields use defaults)

JSON schema for inapp_ad_<xxx>

{
  "enabled": true,
  "native_2id": true,
  "fo_layout": "na_in",
  "return_layout": "na_in",
  "countdown": 5
}
Field Type Default Description
enabled bool true Enable/disable interstitial ad at touchpoint
native_2id bool true Enable/disable native full-screen 2-ID at touchpoint
fo_layout string "na_in" Layout for first-open flow — one of na_in, na_re, na_preparing
return_layout string "na_in" Layout for return-user flow — one of na_in, na_re, na_preparing
countdown long 5 Countdown seconds before auto-close button appears

If the key is absent or the JSON is malformed, all fields fall back to their defaults. Unknown extra fields are ignored (forward-compatible).


Layout Reference

na_in — Native Inline

Standard native ad layout rendered inline inside a full-screen activity. Ad content fills the visible area with a top-bar title and a countdown close button.

na_re — Native Resume

Compact resume-style layout, typically used when returning from background. Lighter visual weight; suited for non-intrusive return-user flows.

na_preparing — Native Preparing

Loading/preparing screen variant. Displays ad while the app performs background work (e.g. resource loading). The countdown timer is visible from the first frame.


Troubleshooting

  • Ad not showing — purchase gate: AppPurchase.getInstance().isPurchased returned true. Expected: no ad for paying users.

  • Ad not showing — remote disabled: Firebase key inapp_ad_<xxx> has "enabled": false. Check key name matches the touchpoint string exactly.

  • Ad not showing — load failed: Both native and interstitial loads failed within the timeout. Check ad unit IDs and network connectivity; ForTester log will show load_failed.

  • Ad not showing — touchpoint not registered: InAppAdManager.initOnce() was not called, or the touchpoint string passed to show() does not match one registered in InAppAdConfig. SDK will throw IllegalStateException.

  • Wrong layout shown: Firebase key inapp_ad_<xxx> has an unrecognised fo_layout or return_layout value. Accepted values: na_in, na_re, na_preparing. SDK falls back to na_in.

  • Reload not triggering after dismiss: 3-second minimum display guard is active. The ad must have been visible for at least 3 s before a reload is enqueued. If the user dismisses faster, no reload fires until the next app-foreground event.

  • Filtering ForTester logs:

    adb logcat | grep FOR_TESTER_FO

    Expected sequence for a successful impression: requestload_startnative_loaded / inter_loadedimpressiondismissedreload_scheduled.

  • Registration checklist log — emitted once at initOnce() time. Cross-check ad unit IDs against Firebase config at a glance:

    [InAppAd] ═══════ Registered 2 touchpoint(s) ═══════
    [InAppAd] ✓ tp=home         native_2id=[ca-app-pub-xxx/native]  inter=[ca-app-pub-xxx/inter]
    [InAppAd] ✓ tp=settings     native_2id=✗  inter=[ca-app-pub-xxx/inter2]
    [InAppAd] ═══════ End registration ═══════

    A means that slot was not registered for this touchpoint (inter-only or native-only). This is intentional and valid as long as at least one slot is present.