# Partner — SDK `1.1.0` Code Integration Guide

Audience: partner developers integrating MonetizeD7 SDK into their app.
Purpose: step-by-step integration — from Gradle setup to verification on a QA build.
Code reconciled: 2026-05-12.

---

## Table of contents

1. [Environment requirements](#1-environment-requirements)
2. [Step 1 — Add the SDK](#2-step-1--add-the-sdk)
3. [Step 2 — Initialize the SDK](#3-step-2--initialize-the-sdk)
4. [Step 3 — Set up Splash config](#4-step-3--set-up-splash-config)
5. [Step 4 — Set up Onboarding](#5-step-4--set-up-onboarding)
6. [Step 5 — Set up Resume Ad](#6-step-5--set-up-resume-ad)
7. [Step 6 — Register InApp touchpoints](#7-step-6--register-inapp-touchpoints)
8. [Step 7 — Set up Splash screen](#8-step-7--set-up-splash-screen)
9. [Step 8 — Customize UI (Splash + Onboarding)](#9-step-8--customize-ui-splash--onboarding)
10. [Step 9 — Show ads at touchpoints](#10-step-9--show-ads-at-touchpoints)
11. [Premium user](#11-premium-user)
12. [Verifying with logs](#12-verifying-with-logs)
13. [Pre-release checklist](#13-pre-release-checklist)

---

## 1. Environment requirements

| Item | Requirement |
|---|---|
| minSdk | 24 |
| compileSdk | 35 |
| JDK | 11 |
| Kotlin | 1.9+ |
| Google Mobile Ads SDK | Transitive dependency (already included) |
| Maven repo | `apero-inhouse` (credentials provided by the team) |

---

## 2. Step 1 — Add the SDK

### 2.1 `settings.gradle.kts`

```kotlin
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
        maven {
            url = uri("<apero-inhouse maven repo URL>")
            credentials {
                username = "<provided>"
                password = "<provided>"
            }
        }
    }
}
```

### 2.2 Module `app/build.gradle.kts`

```kotlin
dependencies {
    implementation("apero-inhouse:monetize-d7:1.1.0")
}
```

---

## 3. Step 2 — Initialize the SDK

### 3.1 Provider class — overview

Create a class implementing `FirstOpenConfigProvider`. This is the single entry point the SDK uses to read every ad config. Treat the helper methods (`buildSplashConfig`, `buildOnboardingConfig`, `buildResumeConfig`, `buildInAppConfig`) and `MyNextActionListener` as a contract — the rest of this guide details each one.

```kotlin
class MyFirstOpenProvider : FirstOpenConfigProvider {

    override fun provideFirstOpenConfig(): FirstOpenConfig =
        FirstOpenConfig.Builder(MyNextActionListener())             // → §3.3
            .setDevelopment(BuildConfig.DEBUG)                      // → §3.4
            .setSplashAdConfig(                                     // → Step 3
                firstOpen  = buildSplashConfigFirstOpen(),
                returnUser = buildSplashConfigReturnUser(),
            )
            .setOnboardingAdConfig(buildOnboardingConfig())         // → Step 4
            .setResumeAdConfig(buildResumeConfig())                 // → Step 5
            .setInAppAd(buildInAppConfig())                         // → Step 6
            .build()

    private fun buildSplashConfigFirstOpen():  FirstOpenConfig.SplashAdConfig      = TODO("see §4")
    private fun buildSplashConfigReturnUser(): FirstOpenConfig.SplashAdConfig      = TODO("see §4")
    private fun buildOnboardingConfig():       FirstOpenConfig.OnboardingAdConfig  = TODO("see §5")
    private fun buildResumeConfig():           FirstOpenConfig.ResumeAdConfig      = TODO("see §6")
    private fun buildInAppConfig():            InAppAdConfig                       = TODO("see §7")
}
```

| Dependency | Type | Detailed in |
|---|---|---|
| `MyNextActionListener()` | `FONextActionListener` | [§3.3 — dispatch after onboarding](#33-fonextactionlistener--dispatch-after-onboarding) |
| `setDevelopment(BuildConfig.DEBUG)` | `Boolean` | [§3.4 — development flag](#34-setdevelopmentbuildconfigdebug--development-flag) |
| `buildSplashConfigFirstOpen()` | `FirstOpenConfig.SplashAdConfig` | [§4 — Step 3](#4-step-3--set-up-splash-config) |
| `buildSplashConfigReturnUser()` | `FirstOpenConfig.SplashAdConfig` | [§4 — Step 3](#4-step-3--set-up-splash-config) |
| `buildOnboardingConfig()` | `FirstOpenConfig.OnboardingAdConfig` | [§5 — Step 4](#5-step-4--set-up-onboarding) |
| `buildResumeConfig()` | `FirstOpenConfig.ResumeAdConfig` | [§6 — Step 5](#6-step-5--set-up-resume-ad) |
| `buildInAppConfig()` | `InAppAdConfig` | [§7 — Step 6](#7-step-6--register-inapp-touchpoints) |

> The Splash setup ships two configs so BA can ship different strategies for FO (new users) and RU (returning users). They must both be `FirstOpenConfig.SplashAdConfig` — same shape, independent values.

### 3.2 Register the provider in `Application.onCreate`

```kotlin
class MyApp : Application() {
    override fun onCreate() {
        super.onCreate()
        FirstOpenSDK.setFirstOpenConfig(MyFirstOpenProvider())
    }
}
```

### 3.3 `FONextActionListener` — dispatch after onboarding

```kotlin
class MyNextActionListener : FONextActionListener {
    override fun dispatch(activity: ComponentActivity, intent: Intent) {
        val mainIntent = Intent(activity, MainActivity::class.java)
        activity.startActivity(mainIntent)
    }
}
```

### 3.4 `.setDevelopment(BuildConfig.DEBUG)` — development flag

Binds the SDK's dev flag to the app's build type. On debug builds, the Apero Ads tester overlay surfaces load / fail / impression events on top of every ad — pairs with the logcat output in [§12](#12-verifying-with-logs). Release builds stay clean.

---

## 4. Step 3 — Set up Splash config

Splash takes **two** configs — one for first-open users, one for returning users — so BA can ship different strategies per segment. Both have the same shape (`FirstOpenConfig.SplashAdConfig`).

```kotlin
private fun buildSplashConfigFirstOpen(): FirstOpenConfig.SplashAdConfig {
    return FirstOpenConfig.SplashAdConfig(
        nativeSplash2ID = NativeAd2Id("ca-app-pub-xxx/fo-native-hf"),
        interSplash2ID  = InterAd2Id("ca-app-pub-xxx/fo-inter-hf"),
        interSplash     = InterAdId("ca-app-pub-xxx/fo-inter-standard"),
        bannerSplash    = BannerAdDouble(
            hf       = "ca-app-pub-xxx/fo-banner-hf",
            standard = "ca-app-pub-xxx/fo-banner-standard",
        ),
        nativeSplash    = NativeAdId("ca-app-pub-xxx/fo-native-standard"),
    )
}

private fun buildSplashConfigReturnUser(): FirstOpenConfig.SplashAdConfig {
    return FirstOpenConfig.SplashAdConfig(
        nativeSplash2ID = NativeAd2Id("ca-app-pub-xxx/ru-native-hf"),
        interSplash2ID  = InterAd2Id("ca-app-pub-xxx/ru-inter-hf"),
        interSplash     = InterAdId("ca-app-pub-xxx/ru-inter-standard"),
        bannerSplash    = BannerAdDouble(
            hf       = "ca-app-pub-xxx/ru-banner-hf",
            standard = "ca-app-pub-xxx/ru-banner-standard",
        ),
        nativeSplash    = NativeAdId("ca-app-pub-xxx/ru-native-standard"),
    )
}
```

---

## 5. Step 4 — Set up Onboarding

```kotlin
private fun buildOnboardingConfig(): FirstOpenConfig.OnboardingAdConfig {
    return FirstOpenConfig.OnboardingAdConfig(
        nativeOnboarding = NativeAdDouble(
            hf = "ca-app-pub-xxx/native-onboarding-hf",
            standard = "ca-app-pub-xxx/native-onboarding-standard",
        ),
    )
}
```

---

## 6. Step 5 — Set up Resume Ad

```kotlin
private fun buildResumeConfig(): FirstOpenConfig.ResumeAdConfig {
    return FirstOpenConfig.ResumeAdConfig(
        appOpenResume = AppOpenAdId("ca-app-pub-xxx/app-open"),
        nativeResume = NativeAdId("ca-app-pub-xxx/native-resume"),
    )
}
```

Which ad type is shown (AppOpen or Native) is controlled by BA via Firebase key `fo_config_ad_resume.type`.

---

## 7. Step 6 — Register InApp touchpoints

Every place in the app that needs to show an ad must register one touchpoint with a unique name.

```kotlin
private fun buildInAppConfig(): InAppAdConfig {
    return InAppAdConfig.Builder()
        .register(
            tp = "home",
            nativeAd2Id = NativeAd2Id("ca-app-pub-xxx/home-native-hf"),
            nativeAdId = NativeAdId("ca-app-pub-xxx/home-native-standard"),
            inter = InterAdId("ca-app-pub-xxx/home-inter"),
        )
        .register(
            tp = "save",
            nativeAd2Id = NativeAd2Id("ca-app-pub-xxx/save-native-hf"),
            nativeAdId = NativeAdId(""),
            inter = InterAdId("ca-app-pub-xxx/save-inter"),
        )
        .build()
}
```

| Convention |
|---|
| Touchpoint name is **lowercase snake_case** (e.g. `home`, `save`, `share`, `settings`) |
| Matching Firebase key: `inapp_ad_<tp>` (e.g. `inapp_ad_home`) |
| Empty ID `""` opts out of that tier |

---

## 8. Step 7 — Set up Splash screen

### 8.1 Subclass `FOSplashActivity`

Extend the SDK's splash activity to set partner-side behavior.

```kotlin
class MySplashActivity : FOSplashActivity()
```

> The Activity reads `FirstOpenSDK.config?.splashAdFirstOpen` / `splashAdReturnUser` at runtime — the ad configs from [Step 3](#4-step-3--set-up-splash-config) wire in automatically. No additional binding needed.

### 8.2 Register as the launcher (`AndroidManifest.xml`)

```xml
<activity
    android:name=".MySplashActivity"
    android:exported="true"
    android:theme="@style/Theme.App.Splash">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity>
```

### 8.3 Override functions if needed

| Hook | When to override |
|---|---|
| `updateUI(savedInstanceState)` | Programmatic UI override (rarely needed — prefer XML resources in Step 8). Call `super` first. |
| `afterFetchRemote()` | Runs once the Firebase remote config snapshot for this session has been fetched and parsed. Use it to kick off any partner-side work that depends on remote flags — e.g. configure feature flags, choose an experiment branch, init A/B-test SDKs, or refresh paywall offers. |
| `suspend interceptorShowFullScreenAd()` | Suspend the splash until a prerequisite action finishes — e.g. wait for a consent dialog, billing handshake, force-update prompt, or A/B-test flag fetch. The SDK runs this concurrently with the ad load and only proceeds to **show the fullscreen ad → navigate to the next screen** after this function returns. |

**Example — `afterFetchRemote` to apply a remote feature flag:**

```kotlin
class MySplashActivity : FOSplashActivity() {

    override fun afterFetchRemote() {
        super.afterFetchRemote()
        FeatureFlags.applyFrom(FirebaseRemoteConfig.getInstance())
    }
}
```

**Example — `interceptorShowFullScreenAd` to block until a consent dialog is dismissed:**

```kotlin
class MySplashActivity : FOSplashActivity() {

    override suspend fun interceptorShowFullScreenAd() {
        ConsentManager.awaitDecision(this)   // suspends until user picks accept / decline
    }
}
```

If a prerequisite never completes, the SDK still respects its splash timeout — the splash will not hang forever.

---

## 9. Step 8 — Customize UI (Splash + Onboarding)

All visual customization is driven by XML resources dropped into `app/src/main/res/`. The SDK looks them up by **exact name** — a typo makes the SDK silently fall back to its default. There is no compile-time check.

This step is split into three parts:

- **§9.1 Overall** — shared color tokens that apply to both Splash and Onboarding.
- **§9.2 Splash** — strings, images, and Lottie animations specific to the Splash screen.
- **§9.3 Onboarding** — strings, images, and Lottie animations specific to Onboarding.

### 9.1 Overall — `res/values/colors.xml`

The entire SDK UI (Splash + Onboarding) derives from these 4 tokens:

```xml
<resources>
    <color name="fo_color_primary">#3B86FF</color>
    <color name="fo_color_background">#FFFFFF</color>
    <color name="fo_color_text_title">#202020</color>
    <color name="fo_color_text_sub_title">#494656</color>
</resources>
```

| Token | What it tints |
|---|---|
| `fo_color_primary` | Brand accent — onboarding `Next` / `Get Started` button, progress dots, loading indicator |
| `fo_color_background` | Window background of the Splash and Onboarding screens |
| `fo_color_text_title` | Title text on the Splash and on each Onboarding slide |
| `fo_color_text_sub_title` | Subtitle / description text on Splash and Onboarding |

### 9.2 Splash

**Strings — `res/values/strings.xml`:**

| Resource name | Default | Partner must set? |
|---|---|---|
| `fo_splash_title` | `@string/app_name` | optional |
| `fo_splash_subtitle` | _(empty)_ | optional |
| `fo_splash_describe_load_ad` | `(This action can contain ads)` | optional |

**Images — `res/drawable/`** (valid extensions: `webp`, `png`, `jpg`):

| Resource name | Partner must set? | Purpose |
|---|---|---|
| `fo_ic_logo_app` | **yes** (unless Lottie supplied) | Splash logo |
| `fo_img_splash_bg` | optional | Splash background |

**Animations — `res/raw/` (optional Lottie override):**

| Resource name | Takes precedence over |
|---|---|
| `fo_anim_logo_app` | `fo_ic_logo_app` |

### 9.3 Onboarding

**Strings — `res/values/strings.xml`:**

| Resource name | Default | Partner must set? |
|---|---|---|
| `fo_onboarding_title_1`, `_2`, `_3` | _(empty)_ | **yes** — one per slide |
| `fo_onboarding_description_1`, `_2`, `_3` | _(empty)_ | **yes** — one per slide |
| `fo_onboarding_button_next` | `Next` | optional |
| `fo_onboarding_button_start` | `Get Started` | optional — shown on the last slide |

**Images — `res/drawable/`** (valid extensions: `webp`, `png`, `jpg`):

| Resource name | Partner must set? | Purpose |
|---|---|---|
| `fo_img_onboarding_1`, `_2`, `_3` | **yes** (unless Lottie supplied) | One image per onboarding slide |

**Animations — `res/raw/` (optional Lottie override):**

| Resource name | Takes precedence over |
|---|---|
| `fo_anim_onboarding_1`, `_2`, `_3` | `fo_img_onboarding_1`, `_2`, `_3` |

---

## 10. Step 9 — Show ads at touchpoints

### 10.1 Preload — how it works

`InAppAdManager.bindPreload(owner, tp)` is lifecycle-aware: it triggers a preload on every `ON_START` of `owner` **if the pool is empty** for that touchpoint, and auto-unregisters on `ON_DESTROY`.

### 10.2 Recommended pattern — wrap each touchpoint in a small object

When the same touchpoint is triggered from several screens, wrap `InAppAdManager` in a per-touchpoint helper object:

```kotlin
object ToolAdManager {

    fun bindPreloadToolAd(activity: FragmentActivity) {
        InAppAdManager.bindPreload(activity, "tool")
    }

    fun showToolAd(activity: FragmentActivity, onNextAction: () -> Unit) {
        InAppAdManager.show(activity, "tool", onNextAction)
    }
}
```

**Bind once in the host screen** (e.g. `MainActivity.onCreate`):

```kotlin
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    ToolAdManager.bindPreloadToolAd(this)
}
```

**Show at every trigger site** — put the navigation / business action in the `onNextAction` lambda:

```kotlin
ToolAdManager.showToolAd(activity) {
    context.startActivity(SelectPhotoActivity.newIntent(context, /* … */))
}
```

| Guarantees |
|---|
| `onNextAction` is **guaranteed to be called** — even when the ad fails, `enabled=false`, or rule-show skips |
| Do not run the action outside the lambda — it would run twice |
| `activity` must be alive at show time |

---

## 11. Premium user

The SDK checks premium state via `AppPurchase` (from `com.ads.control.billing`).

```kotlin
import com.ads.control.billing.AppPurchase

fun onUserUpgradedToPremium() {
    AppPurchase.getInstance().setPurchase(true)
}

fun onUserDowngraded() {
    AppPurchase.getInstance().setPurchase(false)
}
```

| Rule |
|---|
| Set state before the SDK initializes, or immediately when the user completes purchase |
| Premium → the SDK skips every ad call and still invokes `onNextAction` normally |
| No Firebase key needs to change — premium overrides everything |

---

## 12. Verifying with logs

On a debug build, enable logging:

```bash
adb logcat -s SplashAd InAppAd ResumeAd NativeOnboarding BannerSplash FirstOpenSDK
```

Or enable via Firebase: `fo_allow_log_tester=true`.

> Pair this with [`.setDevelopment(BuildConfig.DEBUG)`](#34-setdevelopmentbuildconfigdebug--development-flag) in the provider (§3.4). On debug builds the Apero Ads tester overlay surfaces load / fail / impression events on the ad itself — the overlay messages line up with the logcat lines above, so QA can verify ad behavior visually and via logs at the same time.

Sample log when the SDK initializes successfully:

```
FirstOpenSDK: FirstOpenSDK configured via FirstOpenConfigProvider (sdk=1.1.0)
FirstOpenSDK: Fetch firebase started
FirstOpenSDK: Fetch firebase in config debug (minInterval=30s)
FirstOpenSDK: Fetch firebase successfully in 412ms
FirstOpenSDK: Handle remote config in 18ms
FirstOpenSDK: [InAppAd] ═══════ Registered 4 touchpoint(s) ═══════
FirstOpenSDK: [InAppAd] ✓ tp=home         native=[hf=ca-app-pub.../home-hf, std=ca-app-pub.../home-std]  inter=[ca-app-pub.../home-int]
FirstOpenSDK: [InAppAd] ✓ tp=save         native=[hf=ca-app-pub.../save-hf]                              inter=[ca-app-pub.../save-int]
FirstOpenSDK: [InAppAd] ✓ tp=share        native=[hf=ca-app-pub.../share-hf, std=ca-app-pub.../share-std]  inter=✗
FirstOpenSDK: [InAppAd] ✓ tp=settings     native=✗                                                       inter=[ca-app-pub.../settings-int]
FirstOpenSDK: [InAppAd] ═══════ End registration ═══════
```

What to look for:

| Line | Confirms |
|---|---|
| `FirstOpenSDK configured … (sdk=1.1.0)` | SDK version is `1.1.0` and `setFirstOpenConfig(...)` ran exactly once |
| `Fetch firebase successfully in <ms>` | Remote Config snapshot for this session was retrieved |
| `[InAppAd] Registered N touchpoint(s)` | Every touchpoint passed to `register(...)` was accepted; `N` matches your code |
| `native=[hf=…, std=…]`, `inter=[…]` | Tier-by-tier ID check — `✗` marks a tier you opted out of |

---

## 13. Pre-release checklist

| # | Check | How to verify |
|---|---|---|
| 1 | SDK version is exactly `1.1.0` | Gradle dependency |
| 2 | All Ad Unit IDs match the right environment (test vs prod) | Read registration log |
| 3 | `FirstOpenSDK.setFirstOpenConfig()` called exactly once in `Application.onCreate` | Log `FirstOpenSDK configured...` |
| 4 | `FONextActionListener.dispatch()` starts the correct MainActivity | Run the FO flow |
| 5 | Every `InAppAdManager.show()` has the `onNextAction` lambda | Code review |
| 6 | Premium state set correctly before any ad trigger | Test with a premium account |
| 7 | Touchpoint names match between code and Firebase | Cross-check with BA |
| 8 | Tester logging disabled (`fo_allow_log_tester=false`) on production Firebase | Firebase Console |
| 9 | ProGuard does not obfuscate SDK classes | Build release and test |
| 10 | UI resources from Step 8 present and named correctly (4 colors, logo, onboarding images + strings) | Visual check on QA build |
