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
- Environment requirements
- Step 1 — Add the SDK
- Step 2 — Initialize the SDK
- Step 3 — Set up Splash config
- Step 4 — Set up Onboarding
- Step 5 — Set up Resume Ad
- Step 6 — Register InApp touchpoints
- Step 7 — Set up Splash screen
- Step 8 — Customize UI (Splash + Onboarding)
- Step 9 — Show ads at touchpoints
- Premium user
- Verifying with logs
- 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
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven {
url = uri("<apero-inhouse maven repo URL>")
credentials {
username = "<provided>"
password = "<provided>"
}
}
}
}
2.2 Module app/build.gradle.kts
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.
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 |
setDevelopment(BuildConfig.DEBUG) |
Boolean |
§3.4 — development flag |
buildSplashConfigFirstOpen() |
FirstOpenConfig.SplashAdConfig |
§4 — Step 3 |
buildSplashConfigReturnUser() |
FirstOpenConfig.SplashAdConfig |
§4 — Step 3 |
buildOnboardingConfig() |
FirstOpenConfig.OnboardingAdConfig |
§5 — Step 4 |
buildResumeConfig() |
FirstOpenConfig.ResumeAdConfig |
§6 — Step 5 |
buildInAppConfig() |
InAppAdConfig |
§7 — Step 6 |
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
class MyApp : Application() {
override fun onCreate() {
super.onCreate()
FirstOpenSDK.setFirstOpenConfig(MyFirstOpenProvider())
}
}
3.3 FONextActionListener — dispatch after onboarding
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. 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).
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
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
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.
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.
class MySplashActivity : FOSplashActivity()
The Activity reads
FirstOpenSDK.config?.splashAdFirstOpen/splashAdReturnUserat runtime — the ad configs from Step 3 wire in automatically. No additional binding needed.
8.2 Register as the launcher (AndroidManifest.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. |
isNotificationPermissionEnabled() |
Return true to make the SDK request the POST_NOTIFICATIONS runtime permission during splash (Android 13+). Default is false — no prompt. Runs concurrently with the ad load; splash waits for the user's decision (or denial) before navigating to the next screen. |
Example — afterFetchRemote to apply a remote feature flag:
class MySplashActivity : FOSplashActivity() {
override fun afterFetchRemote() {
super.afterFetchRemote()
FeatureFlags.applyFrom(FirebaseRemoteConfig.getInstance())
}
}
Example — isNotificationPermissionEnabled to ask for notifications during splash:
class MySplashActivity : FOSplashActivity() {
override fun isNotificationPermissionEnabled(): Boolean = true
}
Pair with onNotificationPermissionRequest() / onNotificationPermissionResult(isGranted, isJustRequested) to track the outcome.
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:
<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:
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):
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:
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).
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:
adb logcat -s SplashAd InAppAd ResumeAd NativeOnboarding BannerSplash FirstOpenSDK
Or enable via Firebase: fo_allow_log_tester=true.
Pair this with
.setDevelopment(BuildConfig.DEBUG)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 |