Skip to main content

Kotlin SDK

See how easy it is to integrate high-performance ads into your Android app using our lightweight SDK.

Requirements

  • Min SDK version 26
  • Kotlin 1.9+
  • kotlinx-coroutines 1.8.1+

Getting started

1. Installation

To get started, you will need to set up a publisher account to get a publisherToken and code
The SDK is available on mavenCentral(). Ensure mavenCentral() is listed in your project’s repository configuration.
// settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}
Add the dependency to your app-level build.gradle.kts file or version catalog:
dependencies {
    implementation("so.kontext:ads:1.0.0") // Replace with the latest version
}

Usage

2. Initialize AdsProvider

First, create an instance of AdsProvider using the Builder. This object should be scoped to a single conversation and ideally tied to a lifecycle-aware component, like a ViewModel.
import so.kontext.ads.AdsProvider

val adsProvider = AdsProvider.Builder(
    context = applicationContext, 
    publisherToken = "", // Your unique publisher token from your account manager.
    userId = "user-uuid-123", // A unique string that should remain the same during the user’s lifetime.
    conversationId = "conversation-uuid-456", // Unique ID of the conversation, used for ad pacing.
    enabledPlacementCodes = listOf("inlineAd") // A list of placement codes that identify ad slots in your app.
)
    .variantId("variant-id") // A string provided by the publisher to identify the user cohort in order to compare A/B test groups (optional)
    .advertisingId("advertising-id") // Device-specific identifier provided by the operating systems (GAID)
    .regulatory( // Regulatory compliance object
        Regulatory(
            gdpr = null, // Flag that indicates whether or not the request is subject to GDPR regulations (0 = No, 1 = Yes, null = Unknown).
            gdprConsent = "<gdpr-consent>", // Transparency and Consent Framework's Consent String data structure
            coppa = null, // Flag whether the request is subject to COPPA (0 = No, 1 = Yes, null = Unknown).
            gpp = "<gpp>", // Global Privacy Platform (GPP) consent string
            gppSid = listOf(1, 2), // List of the section(s) of the GPP string which should be applied for this transaction
            usPrivacy = "<us-privacy>" // Communicates signals regarding consumer privacy under US privacy regulation under CCPA and LSPA
        )
    )
    .character( // The character object used in this conversation
        Character(
            id = UUID.randomUUID().toString(), // Unique ID of the character
            name = "John Doe", // Name of the character
            avatarUrl = "", // URL of the character’s avatar
            isNsfw = false, // Whether the character is NSFW
            greeting = "Hello", // Greeting of the character
            persona = "", // Description of the character’s personality
            tags = listOf() // Tags of the character (list of strings)
        )
    )
    .build()

3. Prepare messages

To provide context for ad targeting, your app’s chat messages must be represented in a way the SDK understands. You have two options: Option 1: Conform to MessageRepresentable You can make your existing message data class conform to the MessageRepresentable interface. This involves overriding the required properties to map to your class’s fields.
import so.kontext.ads.MessageRepresentable
import so.kontext.ads.domain.Role

// Your existing chat message class
data class MyChatMessage(
    val uniqueId: String,
    val text: String,
    val author: String, // "user" or "assistant"
    val creationDate: String // e.g., ISO 8601 format
) : MessageRepresentable {

    // Map your properties to the interface requirements
    override val id: String
        get() = uniqueId

    override val role: Role
        get() = if (author == "user") Role.User else Role.Assistant

    override val content: String
        get() = text

    override val createdAt: String
        get() = creationDate
}
Option 2: Use the AdsMessage Data Class If you cannot or prefer not to modify your existing data class, you can map your message objects to the AdsMessage type provided by the SDK. AdsMessage already conforms to MessageRepresentable.
import so.kontext.ads.AdsMessage
import so.kontext.ads.domain.Role

// When you update the SDK, map your list of messages
val messagesForSdk = myChatMessages.map { myMessage ->
    AdsMessage(
        id = myMessage.uniqueId,
        role = if (myMessage.author == "user") Role.User else Role.Assistant,
        content = myMessage.text,
        createdAt = myMessage.creationDate
    )
}

// Then pass this new list to the provider
adsProvider.setMessages(messagesForSdk)

Updating Messages and Collecting Ads

Whenever your list of messages changes, pass the new list to the AdsProvider. The adsProvider.ads property is a kotlinx.coroutines.flow.Flow that emits a Map<String, List<AdConfig>>. The map’s key is the message ID, and the value is a list of ads to be displayed for that message. Collect this flow from a CoroutineScope to receive and display ads.

4. Show your first ad

Once you collected the ads Map in your ViewModel, you can use it in your Composable UI to display the ads. The InlineAd composable is provided for this purpose. It takes an AdConfig object and handles the ad rendering. For View support use InlineAdView.

API documentation

AdsProvider.Builder

Constructor Parameters

context
Context
required
Application context
publisherToken
string
required
Your unique publisher token.
userId
string
required
Unique identifier that remains the same for the user’s lifetime (used for retargeting and rewarded ads).
conversationId
string
required
Unique ID of the conversation.
enabledPlacementCodes
array
Placement codes enabled for the conversation. Example: ['inlineAd'].

Builder Methods

character
object
required
Character object used in this conversation.
variantId
string
Publisher-provided identifier for the user cohort (for A/B testing).
advertisingId
string
Device-specific identifier provided by the operating systems (IDFA).
disabled
boolean
If true, the ads generation will be disabled initially. Can be later enabled by calling enable().
adServerUrl
string
URL of the server from which the ads are served. If no value is provided, this url us used: https://server.megabrain.co
theme
String
Your application theme
regulatory
object
Regulatory object used in this conversation.

InlineAdView view & InlineAd composable properties

config
AdConfig
required
AdConfig object you will receive from the AdsProvider when observing the ads flow.
onEvent
AdEvent
Callback with ad events. All possible types are described below.

AdEvent types

viewed

The user has viewed the ad.
bidId
string
required
Bid id.
content
string
required
Generated ad content.
messageId
string
required
Message ID.
format
string
Format.

clicked

The user has clicked the ad.
bidId
string
required
Bid id.
content
string
required
Generated ad content.
messageId
string
required
Message id.
url
string
required
Click URL.
format
string
Format.
area
string
Area where the user clicked.

renderStarted

Triggered before the first token is received.
bidId
string
required
Bid id.

renderCompleted

Triggered after the last token is received.
bidId
string
required
Bid id.

error

Triggered when an error occurs.
message
string
required
Error message.
errCode
string
required
Error code.

videoStarted

Triggered when the video playback starts.
bidId
string
required
Bid id.

videoCompleted

Triggered when the video playback finishes.
bidId
string
required
Bid id.

rewardGranted

Triggered when the user receives a reward.
bidId
string
required
Bid id.

Generic

payload
Map<String, Any>
required
Key-value pairs of any other possible event

Guides

Lifecycle Management

It is crucial to release the resources used by the SDK. Call the close() method when the AdsProvider is no longer needed.

Example ViewModel Setup

Here is a simplified example of how to integrate the AdsProvider into an Android ViewModel. For a complete, working implementation, please see the example module in SDK repository.
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import so.kontext.ads.AdsProvider
import so.kontext.ads.MessageRepresentable

class ChatViewModel(application: Application) : ViewModel() {

    private val adsProvider: AdsProvider
    private val _messages = MutableStateFlow<List<MyChatMessage>>(emptyList())

    init {
        adsProvider = AdsProvider.Builder(
            context = application,
            publisherToken = "nexus-dev",
            userId = "...",
            conversationId = "...",
            enabledPlacementCodes = listOf("inlineAd")
        ).build()

        // Collect the flow of ads
        viewModelScope.launch {
            adsProvider.ads.collect { result ->
                when (result) {
                    is AdResult.Error -> {
                        // handle error
                    }
                    is AdResult.Success -> {
                        updateMessagesWithAds(result.ads)
                    }
                }
            }
        }
    }

    fun onNewMessage(message: MyChatMessage) {
        // Update your local message list
        val updatedMessages = _messages.value + message
        _messages.value = updatedMessages

        // Pass the updated list to the SDK
        viewModelScope.launch {
            adsProvider.setMessages(updatedMessages)
        }
    }

    override fun onCleared() {
        // IMPORTANT: Clean up SDK resources
        adsProvider.close()
        super.onCleared()
    }
}
I