Skip to content

Kotlin / Android SDK

Native ads, interstitial ads, and rewarded ads for Android apps using Jetpack Compose.

New to Simula?

Head to Getting Started first to create your publisher account, get your API key, and set up your first ad unit. Then come back here to integrate the SDK.

Requirements

  • Android API 24+ (Android 7.0)
  • Jetpack Compose
  • Kotlin 1.9+

Installation

Add the Simula SDK to your module-level build.gradle.kts:

kotlin
dependencies {
    implementation("ad.simula:ad-sdk:1.1.0")
}

The SDK is published to Maven Central. No additional repository configuration is needed.

Manifest Permissions

The SDK declares these permissions automatically (merged into your manifest):

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

To enable the Google Advertising ID for better ad targeting, add this to your app's AndroidManifest.xml:

xml
<uses-permission android:name="com.google.android.gms.permission.AD_ID" />

AD_ID is optional

Without the AD_ID permission, the SDK runs in contextual-only mode. Ads are still served, but targeting relies on contextual signals rather than device identity.

Provider Setup (Compose)

Wrap your app content with SimulaProvider to initialize the SDK and supply configuration to all child composables:

kotlin
import ad.simula.ad.sdk.provider.SimulaProvider

@Composable
fun App() {
    SimulaProvider(
        apiKey = "YOUR_API_KEY",
        devMode = false,
        primaryUserID = "hashed_user_id",
    ) {
        // Your app content
        MainScreen()
    }
}

Provider Parameters

ParameterTypeDefaultDescription
apiKeyStringrequiredYour Simula API key
devModeBooleanfalseEnables development mode — always fills, no billing, excluded from ML targeting, and enables debug console logging. Set to false before shipping
primaryUserIDString?nullHashed user ID for better ad targeting
privacySimulaPrivacyConfig?nullGranular consent configuration. See Privacy
telemetryEnabledBooleantrueEnables SDK performance and error telemetry
adContextSimulaAdContext?nullContextual targeting signals attached to every native ad request

Imperative Initialization

For non-Compose code or early initialization in Application.onCreate():

kotlin
import ad.simula.ad.sdk.ads.SimulaAds

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        SimulaAds.initialize(
            context = this,
            apiKey = "YOUR_API_KEY",
            devMode = false,
            primaryUserID = "hashed_user_id",
        )
    }
}

SimulaAds.initialize() is idempotent -- the first call with a valid key wins; subsequent calls are ignored.

Updating the User ID

Update the primary user identifier at runtime after login or logout:

kotlin
SimulaAds.updatePrimaryUserID("new_hashed_id") // after login
SimulaAds.updatePrimaryUserID(null)             // after logout

Dev mode

Set devMode to true during development. Every ad request returns a test ad (always fills), no impressions are billed, test traffic is excluded from Simula's ML targeting models, and debug logs are printed to the console. Set to false before releasing to production.

Components

ComponentDescriptionGuide
NativeAdSlotInline native ad card for feedsNativeAdSlot
SimulaInterstitialAdFull-screen interstitial adInterstitialAd
SimulaRewardedAdRewarded ad with play-to-earn gateRewardedAd

Privacy

The SDK automatically reads IAB CMP consent values from SharedPreferences — no setup is required for most apps. This entire section is optional. Only configure privacy manually if you need to override the auto-read values or manage consent outside a CMP.

Explicit configuration takes precedence over auto-read values.

kotlin
import ad.simula.ad.sdk.privacy.SimulaPrivacy
import ad.simula.ad.sdk.privacy.SimulaPrivacyConfig

SimulaPrivacy.apply(SimulaPrivacyConfig(
    tcString = "...",
    gdprApplies = true,
    coppaApplies = false,
    enableAdvertisingId = true,
))

SimulaPrivacyConfig

FieldTypeDefaultDescription
tcStringString?nullIAB TCF v2.2 consent string
uspStringString?nullIAB US Privacy (CCPA), e.g. "1YNN"
gppStringString?nullIAB Global Privacy Platform string
gppSidString?nullGPP section IDs, comma-separated
gdprAppliesBoolean?nullWhether GDPR applies (null = unknown)
tcfPurpose1ConsentBoolean?nullExplicit TCF Purpose 1 (storage) consent
coppaAppliesBooleanfalseChild-directed treatment (COPPA)
enableAdvertisingIdBooleanfalseOpt-in for GAID collection

Ad Context

Pass contextual targeting signals via adContext to improve native ad relevance. The customContext field accepts arbitrary key-values — use it to send platform-specific data like recently interacted characters.

kotlin
SimulaProvider(
    apiKey = "YOUR_API_KEY",
    primaryUserID = hashedUserId,
    adContext = SimulaAdContext(
        category = "ai-chat",
        customContext = mapOf(
            "recentCharacters" to listOf(
                mapOf("id" to "reze-01", "name" to "Reze", "description" to "The Bomb Devil from Chainsaw Man"),
                mapOf("id" to "power-02", "name" to "Power", "description" to "The Blood Fiend from Chainsaw Man"),
            ),
            "preferredGenre" to "anime",
        ),
    ),
) {
    MainScreen()
}

Update context at runtime when the user navigates to a new feed or chat:

kotlin
SimulaAds.updateContext(SimulaAdContext(
    category = "ai-chat",
    customContext = mapOf(
        "recentCharacters" to listOf(
            mapOf("id" to "reze-01", "name" to "Reze", "description" to "The Bomb Devil from Chainsaw Man"),
        ),
    ),
))

// Clear context
SimulaAds.updateContext(null)

ProGuard / R8

No manual configuration needed. The SDK bundles its own consumer-rules.pro that is applied automatically during your app's build.

Error Types

All ad load and display failures are reported as SimulaAdError:

ErrorDescription
NotInitializedSDK not initialized. Call SimulaAds.initialize() first
NoSessionSession creation failed
NoFillNo ad available for this placement
NotReadyshow() called before ad finished loading
StaleLoaded ad expired (1-hour limit). Call load() again
DuplicateRequestload() called while a request is already in flight
AlreadyShowingshow() called while an ad is already on screen
NoPresentationContextNo Activity available to present the ad
NetworkNetwork connectivity error
AdUnitNotFoundAd unit ID is not registered for this app (check the publisher dashboard)

AdValue

Revenue data surfaced on onAdPaid callbacks and NativeAdSlot.onPaid. All figures are serve-time estimates derived from the backend floor CPM.

kotlin
data class AdValue(
    val valueMicros: Long,
    val currencyCode: String,
    val precisionType: PrecisionType,
    val expectedCpm: Double,
    val expectedRevenue: Double,
)
FieldTypeDescription
valueMicrosLongPer-impression revenue in micros (5000 = $0.005)
currencyCodeStringISO-4217 currency code, e.g. "USD"
precisionTypePrecisionTypeEstimate quality — currently always ESTIMATED
expectedCpmDoubleEstimated CPM (valueMicros / 1_000)
expectedRevenueDoubleEstimated per-impression revenue (valueMicros / 1_000_000)