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:
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):
<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:
<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:
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
| Parameter | Type | Default | Description |
|---|---|---|---|
apiKey | String | required | Your Simula API key |
devMode | Boolean | false | Enables development mode — always fills, no billing, excluded from ML targeting, and enables debug console logging. Set to false before shipping |
primaryUserID | String? | null | Hashed user ID for better ad targeting |
privacy | SimulaPrivacyConfig? | null | Granular consent configuration. See Privacy |
telemetryEnabled | Boolean | true | Enables SDK performance and error telemetry |
adContext | SimulaAdContext? | null | Contextual targeting signals attached to every native ad request |
Imperative Initialization
For non-Compose code or early initialization in Application.onCreate():
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:
SimulaAds.updatePrimaryUserID("new_hashed_id") // after login
SimulaAds.updatePrimaryUserID(null) // after logoutDev 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
| Component | Description | Guide |
|---|---|---|
NativeAdSlot | Inline native ad card for feeds | NativeAdSlot |
SimulaInterstitialAd | Full-screen interstitial ad | InterstitialAd |
SimulaRewardedAd | Rewarded ad with play-to-earn gate | RewardedAd |
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.
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
| Field | Type | Default | Description |
|---|---|---|---|
tcString | String? | null | IAB TCF v2.2 consent string |
uspString | String? | null | IAB US Privacy (CCPA), e.g. "1YNN" |
gppString | String? | null | IAB Global Privacy Platform string |
gppSid | String? | null | GPP section IDs, comma-separated |
gdprApplies | Boolean? | null | Whether GDPR applies (null = unknown) |
tcfPurpose1Consent | Boolean? | null | Explicit TCF Purpose 1 (storage) consent |
coppaApplies | Boolean | false | Child-directed treatment (COPPA) |
enableAdvertisingId | Boolean | false | Opt-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.
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:
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:
| Error | Description |
|---|---|
NotInitialized | SDK not initialized. Call SimulaAds.initialize() first |
NoSession | Session creation failed |
NoFill | No ad available for this placement |
NotReady | show() called before ad finished loading |
Stale | Loaded ad expired (1-hour limit). Call load() again |
DuplicateRequest | load() called while a request is already in flight |
AlreadyShowing | show() called while an ad is already on screen |
NoPresentationContext | No Activity available to present the ad |
Network | Network connectivity error |
AdUnitNotFound | Ad 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.
data class AdValue(
val valueMicros: Long,
val currencyCode: String,
val precisionType: PrecisionType,
val expectedCpm: Double,
val expectedRevenue: Double,
)| Field | Type | Description |
|---|---|---|
valueMicros | Long | Per-impression revenue in micros (5000 = $0.005) |
currencyCode | String | ISO-4217 currency code, e.g. "USD" |
precisionType | PrecisionType | Estimate quality — currently always ESTIMATED |
expectedCpm | Double | Estimated CPM (valueMicros / 1_000) |
expectedRevenue | Double | Estimated per-impression revenue (valueMicros / 1_000_000) |
