Add iAB Standard Size Experiences to an Android app

Show tag-driven IAB ad slots in an Android app with the iAB Standard Size Experience SDK 0.v3.11.

The iAB Standard Size Experience SDK renders IAB ad units (Interactive Advertising Bureau standard sizes such as 300×250 and 320×50) as Jetpack Compose composables. You add four Gradle artifacts, initialize the SDK with your API key, and place one composable per ad slot with a tag ID. The SDK fetches the creative, plays video and audio ads through Google Interactive Media Ads (IMA), tracks impressions and viewability, and reports analytics on its own.

A tag ID identifies one ad placement configured in Brand Control Center, so campaigns, creatives, and targeting change without an app update. This page covers the native Android SDK. The React Native wrapper is described in Add iAB Standard Size Experiences to a React Native app.

ArtifactAAR sizeAddsRequired
com.begenuin:genuin-sdk8 KBGenuinSDK.initialize, setLoggingEnabled, setGeoLocationYes
com.begenuin:feed2.7 MBThe eight ad view composables in com.begenuin.feed.viewsYes
com.begenuin:instream-ads69 KBThe ad runtime: ad retrieval, VAST playback through IMA, viewability, macro resolution. No public API; core-v3 discovers it at runtime through ServiceLoader.Yes
com.begenuin:core-v33.3 MBShared public types (BGInstreamAdEvent, BGGeoLocationInfo, GenuinEnv, AudioBehaviorConfig), networking, the player, analyticsYes
com.begenuin:pangle-ads57 KB, plus the Pangle SDK 8.3.0.3Fills slots from Pangle (ByteDance)Optional
com.begenuin:gma-ads58 KB, plus the Google Mobile Ads SDK 1.3.1Fills slots with Google native adsOptional

Sizes are the published AAR files before their dependencies. Keep every artifact on the same version.

Requirements

Minimum
AndroidAPI 24 (Android 7.0). Every module declares minSdkVersion 24.
compileSdk and targetSdk36
Android Gradle Plugin8.9.1 or later. The SDK itself is built with 8.13.2.
Gradle8.13, or the version your AGP requires
Kotlin2.0.21 or later. The AARs carry Kotlin 2.0 metadata, which Kotlin 1.9 can't read.
Java17 for sourceCompatibility, targetCompatibility, and jvmTarget
UI toolkitJetpack Compose, BOM 2024.09.00 or later. View-based apps host the composables in a ComposeView.
Core-library desugaringRequired by the Media3 IMA extension
DevicesPhones, tablets, and foldables on arm64-v8a, armeabi-v7a, and x86_64. Pangle fills on arm64-v8a and armeabi-v7a only.

You also need an API key and at least one tag ID. See Step 5.

Step 1: Add the repositories

The four artifacts are on Maven Central, and their dependencies come from Google's repository and Maven Central. Declare both in your project settings:

settings.gradle.kts
kotlin
dependencyResolutionManagement {
  repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
  repositories {
    google()
    mavenCentral()
    // Only with com.begenuin:pangle-ads. Pangle isn't on Maven Central.
    maven {
      url = uri("https://artifact.bytedance.com/repository/pangle/")
      content { includeGroupByRegex("com\\.pangle.*") }
    }
  }
}

A POM can't declare a repository, so the app has to. Without the ByteDance repository, a build that includes pangle-ads fails to resolve com.pangle.global:pag-sdk.

Step 2: Add the dependencies

Declare one version and apply it to every artifact:

app/build.gradle.kts
kotlin
val genuinVersion = "0.v3.11"

dependencies {
  implementation("com.begenuin:genuin-sdk:$genuinVersion")
  implementation("com.begenuin:feed:$genuinVersion")
  implementation("com.begenuin:core-v3:$genuinVersion")
  implementation("com.begenuin:instream-ads:$genuinVersion")   // never imported; required at runtime
  // implementation("com.begenuin:pangle-ads:$genuinVersion")  // optional: Pangle fill
  // implementation("com.begenuin:gma-ads:$genuinVersion")     // optional: Google Mobile Ads fill
}

Three things about this block:

  • Declare core-v3 yourself. feed lists it at runtime scope only. Without your own line, BGInstreamAdEvent and BGGeoLocationInfo aren't on the compile classpath and the build fails with an unresolved reference.
  • instream-ads has no public API. core-v3 finds it through ServiceLoader at runtime. Without it, every slot renders "No ads available" and nothing is logged.
  • The ad-network modules need no code. Add pangle-ads or gma-ads only if Genuin has enabled that network for your tags. Each module registers itself through META-INF/services; there's no initialization call. Don't add the Pangle or Google Mobile Ads SDKs yourself, because they arrive transitively at the versions the SDK is tested with.

Caution: Pin the exact version. genuin-sdk, feed, and core-v3 also publish a stray 0.v.1.q that Maven Central reports as latest, so a + range or a version-catalog latest.release resolves to the wrong artifact.

The SDK brings Media3 1.9.1 (ExoPlayer, HLS, and the IMA extension), Compose BOM 2024.09.00, and Material 1.13.0. It also pulls in Retrofit 2.9.0, kotlinx.serialization 1.7.3, Coil 2.6.0, Lottie Compose 4.0.0, Dagger 2.59.2, and the RudderStack Kotlin SDK 1.1.0. If your app already uses any of these, Gradle resolves to the higher version.

After a sync, confirm the wiring with ./gradlew :app:dependencies --configuration releaseRuntimeClasspath. The tree must show the four com.begenuin artifacts and androidx.media3:media3-exoplayer-ima.

Step 3: Enable Java 17, Compose, and desugaring

Ad playback runs on Google IMA, which arrives through androidx.media3:media3-exoplayer-ima:1.9.1. IMA uses java.time, so each app module must enable core-library desugaring itself; Gradle doesn't inherit it from a library.

app/build.gradle.kts
kotlin
plugins {
  id("org.jetbrains.kotlin.plugin.compose")   // the Kotlin 2.x Compose compiler
}

android {
  buildFeatures { compose = true }
  compileOptions {
    isCoreLibraryDesugaringEnabled = true
    sourceCompatibility = JavaVersion.VERSION_17
    targetCompatibility = JavaVersion.VERSION_17
  }
  kotlinOptions { jvmTarget = "17" }
}

dependencies {
  coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
}

Without desugaring, the build fails with Invoke-customs are only supported starting with Android O (--min-api 26), or the app crashes on the first ad with NoClassDefFoundError: Failed resolution of: Ljava/time/Duration;.

Step 4: Configure the manifest

The library manifests don't declare network permissions. Add both to your AndroidManifest.xml, and register the Application subclass you'll initialize from:

AndroidManifest.xml
xml
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

<application android:name=".MyApp">
  <!-- your activities -->
</application>

ACCESS_NETWORK_STATE backs the SDK's connectivity check. It also arrives transitively today, so declare it anyway in case those dependencies change.

Two things merge in from the SDK without any work on your side:

  • The advertising ID permission. instream-ads declares com.google.android.gms.permission.AD_ID to resolve the [DID], [LIMITED_AD_TRACKING], and [DNT] macros in VAST tags. It's granted at install with no prompt. Declare advertising-ID use in the Play Console Data safety form, or the submission is rejected. To drop the permission, and with it those macros, add <uses-permission android:name="com.google.android.gms.permission.AD_ID" tools:node="remove" />.
  • Two activities. BGFullScreenActivity from feed presents fullscreen playback, and WebViewActivity from core-v3 opens CTA landing pages in an in-app browser.

Ad assets travel over HTTPS, so no network-security-config or usesCleartextTraffic is needed. The SDK never shows a consent or tracking prompt. In a GDPR or ePrivacy region, collect consent through your own consent management platform before you request ads.

Step 5: Get your credentials

CredentialPurposeWhere to get it
API keyAuthenticates your app. Passed once to initialize.Brand Control Center: Build > Publish Brand Community > SDK Integration, where the Live Environment and QA Environment tabs each show their key
Tag IDIdentifies one ad placement. Passed per ad slot.The id of an iAB Standard Tag created in Brand Control Center under Grow > Off Site > Extended > iAB Standard Tag and accepted for your brand. Your Genuin team passes it to you.

Keys aren't shared between environments: a Live key with GenuinEnv.QA returns no ads. Keep the key out of source control. Inject it from local.properties into a BuildConfig field, from a Gradle secrets plugin, or from your CI secrets.

Step 6: Initialize the SDK

Call GenuinSDK.initialize once, in Application.onCreate, before any ad slot composes. A slot that composes first throws IllegalStateException: SDK not initialized.

MyApp.kt
kotlin
import android.app.Application
import com.begenuin.core.enums.GenuinEnv
import com.begenuin.core.model.AudioBehaviorConfig
import com.begenuin.core.model.AudioFocusLossHandling
import com.begenuin.sdk.GenuinSDK

class MyApp : Application() {
  override fun onCreate() {
    super.onCreate()
    GenuinSDK.initialize(
      context = this,
      apiKey = BuildConfig.GENUIN_API_KEY,   // injected at build time, never a literal
      env = GenuinEnv.PROD,                  // GenuinEnv.QA only with a QA key
      fontFamily = "",                       // optional
      audioBehaviorConfig = AudioBehaviorConfig(
        duckVolumeDuringTransientCanDuck = 0.15f,
        focusLossHandling = AudioFocusLossHandling.MUTE_OUTPUT
      )
    )
    GenuinSDK.setLoggingEnabled(BuildConfig.DEBUG)
  }
}
ParameterTypeRequiredDefaultDescription
contextApplicationYes—Your application instance
apiKeyStringYes—The brand API key from Step 5
envGenuinEnvYes—PROD or QA. Must match the environment the key and the tag belong to.
fontFamilyStringNo""A font family for text the SDK renders. Empty keeps the SDK default.
audioBehaviorConfigAudioBehaviorConfigNoAudioBehaviorConfig.DEFAULTHow playback reacts to audio-focus changes

AudioBehaviorConfig has two fields:

FieldTypeDefaultDescription
duckVolumeDuringTransientCanDuckFloat0.15fVolume held while another app takes transient audio focus. Must be between 0 and 1; any other value throws IllegalArgumentException at initialization.
focusLossHandlingAudioFocusLossHandlingMUTE_OUTPUTMUTE_OUTPUT keeps playing with the output muted; PAUSE_PLAYBACK pauses the ad.

setLoggingEnabled(true) turns on the SDK's diagnostic log under the GenuinSDK logcat tag. The log is off in every published build whatever your own build type, and errors are logged either way. Gate it on debug builds, because the log names your tag IDs.

Step 7: Display an ad

Pick the composable that matches the slot size, place it in your layout, and pass the tag ID. The SDK requests the feed, renders the creative, and tracks the impression.

ArticleScreen.kt
kotlin
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.begenuin.feed.views.BG300x250AdView

@Composable
fun ArticleScreen() {
  Column {
    ArticleBody()
    BG300x250AdView(
      tagId = "{{TAG_ID}}",
      modifier = Modifier.fillMaxWidth(),
    )
  }
}

All eight ad views share one signature:

Initializer
kotlin
@Composable
fun BG300x250AdView(
  tagId: String,
  modifier: Modifier = Modifier,
  uniqueId: String = "",
  geoLocation: BGGeoLocationInfo? = null,
  onEvent: (BGInstreamAdEvent) -> Unit = {},
)
ParameterTypeRequiredDefaultDescription
tagIdStringYes—The tag to load. A blank value shows the placeholder, makes no request, and emits no event.
modifierModifierNoModifierPlacement only (padding, alignment). Seven formats draw at their own dp size inside it; BGFSLandscapeAdView fills it.
uniqueIdStringNo""Set a different value for each slot that shares a tag and size on one screen. Per-slot state is keyed on iab_<tagId>_<format>_<uniqueId>.
geoLocationBGGeoLocationInfo?NonullGeo targeting for this slot only. See Step 8.
onEvent(BGInstreamAdEvent) -> UnitNo{}Lifecycle events. See Step 9.

The slot keeps its declared size in every state, so your layout doesn't shift while the ad resolves. It shows a shimmer while loading, the ad pager once the feed resolves, and a dark box reading "No ads available" when it doesn't. The SDK never collapses an empty slot. To reclaim the space, handle NoAds in onEvent and hide the container yourself.

Host an ad in a View-based screen

Wrap the composable in a ComposeView. Your module still needs the Compose plugin and buildFeatures { compose = true } from Step 3.

activity_article.xml
xml
<androidx.compose.ui.platform.ComposeView
    android:id="@+id/adSlot"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" />
ArticleActivity.kt
kotlin
findViewById<ComposeView>(R.id.adSlot).setContent {
  BG320x50AdView(tagId = "{{TAG_ID}}")
}

The composition must run inside an Activity. Fullscreen and the CTA link-out read LocalContext as an Activity; with an application context the fullscreen button has no effect and the link-out throws.

Ad formats

ComposableSlot size (dp)PresentationNotes
BG320x50AdView320 × 50RowStandard banner
BG320x100AdView320 × 100RowLarge banner
BG300x250AdView300 × 250Full-bleedMedium rectangle
BGCollapsibleAdView300 × 250, collapsing to 300 × 50Full-bleed, then rowA chevron in the control bar collapses and re-expands the slot with a 300 ms animation. Playback continues while collapsed. Each toggle emits Collapsed.
BG320x480AdView320 × 480Full-bleedHalf-page portrait
BG300x600AdView300 × 600Full-bleedHalf-page filmstrip
BG1280x720AdView1280 × 720LandscapeFixed 16:9. A host narrower than 1280 dp clamps the width, so give it a wide box.
BGFSLandscapeAdViewFills its boxLandscapeThe same landscape design scaled to the box the modifier gives it. Bound one axis and the other follows at 16:9.

Seven formats render at their IAB size in dp regardless of the container. A host narrower than the slot clamps it rather than letting it overflow. Give each placement at least the slot's width: 320 dp for the 320-wide slots and 300 dp for the 300-wide ones.

BGFSLandscapeAdView honours the ratio you ask for between 1.25:1 and 2.4:1. A box outside that band is letter-boxed or pillar-boxed to the nearest allowed ratio, so the slot can render smaller than its box.

The eight formats map to three presentations, chosen by the composable you place:

PresentationFormatsWhat the viewer sees
Full-bleed300 × 250, the expanded collapsible, 320 × 480, 300 × 600The creative scaled to the whole slot. Video ads show the brand logo top-left and the control bar top-right; audio ads show a brand-coloured background with a centred caption, an "Ad · remaining" pill, and a progress bar.
Row320 × 100, 320 × 50, the collapsed collapsibleThe playing video on the left with the brand name, description, and the Watch and CTA buttons beside it
Landscape1280 × 720, flexible landscapeAudio ads show a centred portrait video panel flanked by advertiser and QR cards; video creatives render full-bleed. A box too narrow for the side cards falls back to full-bleed.

Fullscreen, mute, and play/pause controls appear in every size unless the tag's configuration in Brand Control Center hides them. The two landscape formats skip the loading shimmer and stay on a plain backdrop until the ad is ready.

Step 8: Supply geo targeting (Optional)

By default, the SDK derives the viewer's location from the request IP, so there's nothing to do. If your app knows the location more precisely, pass it, and each field you set replaces the IP-derived value. Most apps set it once, app-wide, wherever the location becomes known:

Geo targeting
kotlin
import com.begenuin.core.model.BGGeoLocationInfo
import com.begenuin.sdk.GenuinSDK

GenuinSDK.setGeoLocation(
  BGGeoLocationInfo(
    country = "US",
    region = "NY",
    city = "New York",
    dmaCode = "501",
    latitude = 40.7128,
    longitude = -74.0060,
  )
)

setGeoLocation is independent of initialize: call it before or after slots compose, from any thread, as often as the value changes. Pass null to clear it. For one placement that needs different targeting, pass geoLocation to that ad view; its fields win over the app-wide value one by one.

FieldTypeMacros resolvedFallback
countryString?[COUNTRY_ID], [USER_GEO_COUNTRY]IP lookup
regionString?[REGION], [USER_GEO_REGION]IP lookup
cityString?[CITY], [USER_GEO_CITY]IP lookup
dmaCodeString?[DMA_CODE], [USER_GEO_DMA]None. DMA targeting happens only if you pass it.
latitudeDouble?[LOCATION_LAT], [USER_GEO_LAT]IP lookup
longitudeDouble?[LOCATION_LON], [USER_GEO_LON]IP lookup

Three rules govern how values resolve. The slot's geoLocation wins over the app-wide value, which wins over the IP lookup, field by field; a null or blank field falls through to the next source. Each ad request reads the geo current when it builds its URL, so a late location still reaches every request that hasn't gone out, prefetched ones included. Requests already in flight keep their geo; no ad is re-requested.

Setting both latitude and longitude also reports the geo type ([USER_GEO_TYPE], [DEVICE_GEO_TYPE]) as device location rather than IP-derived. The SDK never asks for the location permission and never reads device location itself. If you pass coordinates, your app owns the permission, the consent, and the matching Data safety declaration.

Step 9: Observe lifecycle events

Pass an onEvent lambda to any ad view. The SDK emits BGInstreamAdEvent values as the slot loads its feed and plays through its ads:

Lifecycle events
kotlin
BG300x250AdView(
  tagId = "{{TAG_ID}}",
  onEvent = { event ->
    when (event) {
      BGInstreamAdEvent.Loading -> {}                 // feed request started
      is BGInstreamAdEvent.Loaded -> {}               // event.count ads to play
      BGInstreamAdEvent.NoAds -> hideAdContainer()    // reclaim the space yourself
      is BGInstreamAdEvent.Appeared -> {}             // ad event.index of event.total is on screen
      is BGInstreamAdEvent.Started -> {}              // playback began
      is BGInstreamAdEvent.Completed -> {}            // this ad played to the end
      is BGInstreamAdEvent.Changed -> {}              // pager moved event.from -> event.to
      is BGInstreamAdEvent.LoadTimeout -> {}          // still loading after event.timeoutMs
      is BGInstreamAdEvent.NoFill -> {}               // this ad's waterfall produced nothing
      is BGInstreamAdEvent.AllAdsCompleted -> {}      // nothing left to play in the slot
      is BGInstreamAdEvent.Collapsed -> {}            // event.isCollapsed is the new state
    }
  },
)

BGInstreamAdEvent is a sealed interface with 11 cases. Ad positions are 1-based.

EventPayloadFires when
Loading—The slot began loading its feed
Loadedcount: IntThe feed resolved to count renderable ads
NoAds—The feed resolved but produced nothing renderable, the request failed, or instream-ads is missing
Appearedindex, total, typeAd index of total became the active cell
Startedindex, total, typeThe creative resolved and playback began
Completedindex, total, typeAd index played its media to the end. Only a real playout reports it; an ad that never filled reports NoFill instead.
Changedfrom, toThe pager moved between ads
LoadTimeoutindex, total, type, timeoutMs: LongAd index hadn't resolved within its 500 ms waterfall budget. Informational only: the waterfall continues and still reports Started or NoFill.
NoFillindex, total, typeAd index was tried but its waterfall produced no fill
AllAdsCompletedtotal: IntThe last ad in the slot finished or failed to fill, so nothing is pending. Fires once per pass; a revisited slot that resolves a fresh ad reports it again.
CollapsedisCollapsed: BooleanThe viewer collapsed or expanded BGCollapsibleAdView. Never fires for the fixed sizes.

type labels the kind of ad the slot led with. The values are audio (a VAST audio ad), external (filled by Pangle or Google Mobile Ads), sponsored (a sponsored post), video (a video-content ad), and unknown. Mute changes, Watch and CTA taps, and fullscreen enter and exit aren't surfaced as events; the SDK reports them through its own analytics (see Analytics events).

Step 10: Keep ads working in release builds

The 0.v3.11 AARs ship no consumer ProGuard rules; their proguard.txt files are empty. Two mechanisms inside the SDK break under R8 full mode, the default since AGP 8. The ServiceLoader registrations in META-INF/services name provider classes as text, and Retrofit reflects on generic signatures that R8 strips. The symptom is "No ads available" in release builds only, with ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType in logcat and an "Ad feed request returned no data" line after a few milliseconds.

Add these rules to your app's proguard-rules.pro:

proguard-rules.pro
proguard
#--- iAB Standard Size Experience SDK: providers and serializers are resolved by class name
-keep class com.begenuin.** { *; }
-keep interface com.begenuin.** { *; }
-dontwarn com.begenuin.**

#--- Google IMA (VAST playback)
-keep class com.google.ads.interactivemedia.** { *; }
-dontwarn com.google.ads.interactivemedia.**

#--- Retrofit under R8 full mode: keep the generic signatures it reflects on
-keep,allowobfuscation,allowshrinking interface retrofit2.Call
-keep,allowobfuscation,allowshrinking class retrofit2.Response
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation
-keepattributes Signature, InnerClasses, EnclosingMethod, RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
-if interface * { @retrofit2.http.* <methods>; }
-keep,allowobfuscation interface <1>

These are the rules the React Native wrapper ships as consumer rules and checks against a minified release build. The broad com.begenuin.** keep trades APK size for certainty. Verify ad rendering in a build with isMinifyEnabled = true before you release.

Troubleshooting

SymptomCauseFix
Build fails with Module was compiled with an incompatible version of KotlinYour Kotlin plugin is below 2.0.21Upgrade the Kotlin Gradle plugin to 2.0.21 or later
Build fails with class file has wrong version 61.0 or Inconsistent JVM-target compatibilityJava or JVM target below 17Set sourceCompatibility, targetCompatibility, and jvmTarget to 17 (Step 3)
Invoke-customs are only supported starting with Android O, or NoClassDefFoundError for a java.time class at runtimeCore-library desugaring is offEnable desugaring and add desugar_jdk_libs (Step 3)
Unresolved reference: BGInstreamAdEventcore-v3 isn't declaredAdd com.begenuin:core-v3 (Step 2)
Could not resolve com.begenuin:...google() or mavenCentral() is missingDeclare both repositories (Step 1)
Could not find com.pangle.global:pag-sdkThe ByteDance repository is missingAdd it to settings.gradle.kts (Step 1)
Crash with IllegalStateException: SDK not initializedA slot composed before initialize ranInitialize in Application.onCreate (Step 6)
Every slot shows "No ads available" and no Loading event firesThe tagId is blankPass a non-empty tag ID
Every slot shows "No ads available" after Loadinginstream-ads isn't on the classpath, the key or env is wrong, or the tag is inactiveCheck the dependency, the key and environment pair, and the tag; turn on setLoggingEnabled(true) and read the GenuinSDK tag
Ads never load and no network request is madeINTERNET isn't declaredAdd the permission (Step 4)
Ads work in debug but not in a minified release buildR8 stripped provider registrations or Retrofit signaturesAdd the keep rules (Step 10)
The fullscreen button has no effect, or the CTA crashesThe composition isn't hosted in an ActivityHost the ad in an Activity-backed composition
Pangle or Google Mobile Ads never fillThe module is missing, Pangle is on an x86 emulator, or your package isn't registered with PangleLook for BGAdNetworkRegistry: ad networks available = [...] in the log; test Pangle on an arm64 device. Pangle error 40060 means the app isn't registered on Pangle's platform, not a no-fill.

App size

ArtifactAAR size
genuin-sdk8 KB
feed2.7 MB
instream-ads69 KB
core-v33.3 MB
Total, required modules6.0 MB before dependencies

Media3 and the Google IMA SDK add to that, and are shared if your app already uses them. The optional pangle-ads and gma-ads modules each add their network's SDK. Enabling R8 reduces all of these figures.

Analytics events

The SDK reports the ad lifecycle through its own analytics pipeline (RudderStack) with no wiring on your side. It sends 21 events, which appear in Brand Control Center reporting and in any analytics destination you connect.

GroupEvents
TagTag Initialized, Tag Rendered, Tag Viewed
RequestAd Requested, Ad Response Received, Ad Request Failed, Api Failure
RenderingAd Impression, Ad Viewable Impression, Ad Rendered, Ad Rendered Failed
PlaybackAd Media Play, Ad Played, Ad Paused, Ad Media Quartile, Ad Completed, Playback Failed
InteractionAd Muted, Ad Unmuted, Ad Clicked
ErrorsAd Error

Limitations

  • Android only, API 24 and later: there's no View-based ad view; View apps host the composables in a ComposeView.
  • One version for all artifacts: genuin-sdk, feed, instream-ads, and core-v3 must share a version, and a + range resolves to the wrong artifact.
  • Fixed slot sizes: seven formats draw at their IAB dp size and clamp in a narrower host; only BGFSLandscapeAdView adapts, and only between 1.25:1 and 2.4:1.
  • No pause or resume API: playback follows the Android lifecycle of the host Activity.
  • No consumer ProGuard rules in 0.v3.11: add the rules in Step 10 or release builds show no ads.
  • Pangle is arm-only and always personalised: it never fills on x86 emulators, and the module requests personalised ads without a consent signal. Talk to Genuin before shipping Pangle in a consent region.
  • Empty slots stay on screen: the SDK doesn't collapse a slot with no ads; handle NoAds yourself.

See also

TRUST AND COMPLIANCE

© 2026 Genuin Inc.

Genuin Footer