Android SDK
The Monetai Android SDK allows you to integrate our purchase prediction engine directly into your app. This guide will walk you through the entire process, from installation to using key features like event tracking and purchaser prediction.
Prerequisitesβ
- Android API Level
21(Android 5.0) or higher - Google Play Billing Library v8 (for SDK version 2.0.0+)
- Google Play Billing Library v7 (for SDK version 1.x)
π Additional Resourcesβ
- GitHub Repository: https://github.com/hayanmind/monetai-android
- Example Apps: Check out various integration examples in the
examplesfolder of the GitHub repository
π§ Language Supportβ
The Monetai Android SDK supports both Kotlin and Java. Select the appropriate tab below based on your project's language.
Essential SDK Setupβ
π This section covers the essential steps to install the Monetai SDK and configure it to collect the data needed for purchase predictions.
Please make sure to follow all three steps to lay the foundation for all promotion features.
1. SDK Installationβ
Add the Monetai Android SDK to your project using Gradle.
// Add JitPack repository
repositories {
maven { url 'https://jitpack.io' }
}
// Add dependency
dependencies {
implementation 'com.github.hayanmind:monetai-android:2.0.0'
}
For SDK version 2.0.0 and above:
- Includes Google Play Billing Library v8 internally
- Requires Google Play Billing Library v8 or higher
For SDK version 1.x:
- Includes Google Play Billing Library v7 internally
- Compatible with Google Play Billing Library v7
Migration from v1.x to v2.0.0:
- No code changes required for your app
- Gradle automatically handles dependency resolution
- Important: If using other billing libraries, ensure they support Billing Library v8
2. SDK Initializationβ
Initialize the SDK when your app launches.
- Kotlin
- Java
import com.monetai.sdk.MonetaiSDK
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Initialize Monetai SDK
MonetaiSDK.shared.initialize(
context = this@MainActivity,
sdkKey = "YOUR_SDK_KEY", // SDK key issued from Monetai dashboard
userId = "USER_ID" // User ID of your app's user
) { result, error ->
if (error != null) {
println("Monetai SDK initialization failed: ${error.message}")
} else {
println("Monetai SDK initialization completed: $result")
}
}
}
}
API Reference
Parameters
| Parameter | Type | Description |
|---|---|---|
| context | Context | Application context |
| sdkKey | string | SDK key issued from Monetai (Settings > SDK Integration) |
| userId | string | User ID of your app's user (if not available, use a unique identifier such as email or device ID) |
| completion | callback? | Completion callback called when initialization is complete (optional) |
Callback Parameters
| Parameter | Type | Description |
|---|---|---|
| result | InitializeResult? | Result when initialization succeeds |
| error | Exception? | Error when initialization fails |
InitializeResult Type
| Return Value | Type | Description |
|---|---|---|
| organizationId | number | Organization ID |
| platform | 'android' | Platform info |
| version | string | SDK version |
| userId | string | Set user ID |
| group | ABTestGroup | null | A/B test group |
import com.monetai.sdk.MonetaiSDKJava;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Initialize Monetai SDK
MonetaiSDKJava.getShared().initialize(
this, // Application context
"YOUR_SDK_KEY", // SDK key issued from Monetai dashboard
"USER_ID", // User ID of your app's user
(result, error) -> {
if (error != null) {
Log.e("MonetaiSDK", "Initialization failed: " + error.getMessage());
} else {
Log.d("MonetaiSDK", "Initialization completed: " + result);
}
}
);
}
}
API Reference
Parameters
| Parameter | Type | Description |
|---|---|---|
| context | Context | Application context |
| sdkKey | string | SDK key issued from Monetai (Settings > SDK Integration) |
| userId | string | User ID of your app's user |
| completion | callback? | Completion callback called when initialization is complete (optional) |
Callback Parameters
| Parameter | Type | Description |
|---|---|---|
| result | InitializeResult? | Result when initialization succeeds |
| error | Exception? | Error when initialization fails |
InitializeResult Type
| Return Value | Type | Description |
|---|---|---|
| organizationId | number | Organization ID |
| platform | 'android' | Platform info |
| version | string | SDK version |
| userId | string | Set user ID |
| groupString | string | null | A/B test group (string) |
ABTestGroup Type
| Value | Description |
|---|---|
| monetai | Experimental group with automatic promotion |
| baseline | Control group without promotion |
| unknown | Users not included in campaign |
| null | When no current campaign exists |
3. Logging User Eventsβ
This is a critical step. Monetai's AI model relies entirely on the user events you log to analyze behavior and predict purchase intent. Without this data, the prediction feature will not function.
- Kotlin
- Java
import com.monetai.sdk.MonetaiSDK
// Basic event logging
MonetaiSDK.shared.logEvent("app_in")
// Event logging with parameters
MonetaiSDK.shared.logEvent(
eventName = "screen_in",
params = mapOf("screen" to "home")
)
API Reference
Parameters
| Parameter | Type | Description |
|---|---|---|
| eventName | string | Event name |
| params | Map<String, Any>? | Event parameters (optional) |
import com.monetai.sdk.MonetaiSDKJava;
// Basic event logging
MonetaiSDKJava.getShared().logEvent("app_in");
// Event logging with parameters
Map<String, Object> params = new HashMap<>();
params.put("screen", "home");
MonetaiSDKJava.getShared().logEvent("screen_in", params);
API Reference
Parameters
| Parameter | Type | Description |
|---|---|---|
| eventName | string | Event name |
| params | Map<String, Any>? | Event parameters (optional) |
- Log all in-app events. Monetai will automatically select events most relevant to non-payer prediction for training.
- If no events are logged, instrument events on key features or buttons, especially those that occur before a purchase.
Events logged before the SDK is initialized are queued and sent automatically once initialization is complete.
Important: Even with the same event name, different parameters create different events that the AI model recognizes separately. Using too many diverse parameter values can reduce model accuracy.
Best Practices:
- β
Good:
{ screen: "home" },{ button: "upgrade" },{ category: "premium" } - β Avoid:
{ timestamp: "2024-01-15T10:30:00Z" },{ userId: "user123" },{ sessionId: "abc123" }
Why? Parameters like timestamps, user IDs, or session IDs create unique events for each occurrence, making it difficult for the model to find patterns. Focus on parameters that represent user behavior categories rather than specific instances.
Implementing Promotionsβ
π With the essential setup complete, this section dives into implementing the core promotion logic.
You'll learn how to trigger purchase predictions and use the results to display your custom promotional UI.
1. Predicting Purchasesβ
Call the predict() function at a key purchase decision moment within your app to predict whether a user is likely to make a purchase.
- To optimize the user experience and most clearly measure campaign performance, we strongly recommend calling the
predict()function at only one specific point in the user journey. - It's most effective to call it at the critical moment of hesitationβafter the user has recognized the product's value but is still deciding whether to purchase.
Depending on your app's characteristics, consider points like:- When the user navigates away from the full-price subscription page.
- When the user completes a core workflow or task.
- When the user attempts to access or use a premium feature.
- Kotlin
- Java
import com.monetai.sdk.MonetaiSDK
fun predictUserPurchase() {
MonetaiSDK.shared.predict { result, error ->
if (error != null) {
println("Prediction failed: ${error.message}")
return@predict
}
println("Prediction result: ${result?.prediction}")
println("Test group: ${result?.testGroup}")
when (result?.prediction) {
PredictResult.NON_PURCHASER -> {
// When predicted as non-purchaser, offer discount
println("Predicted as non-purchaser - discount can be applied")
}
PredictResult.PURCHASER -> {
// When predicted as purchaser
println("Predicted as purchaser - discount not needed")
}
null -> {
// Cannot predict
println("Cannot predict - no active campaign or model")
}
}
}
}
API Reference
Return Value
| Return Value | Type | Description |
|---|---|---|
| prediction | PredictResult | Prediction result |
| testGroup | ABTestGroup | A/B test group |
PredictResult Type
| Value | Description |
|---|---|
| NON_PURCHASER | Predicted not to purchase |
| PURCHASER | Predicted to purchase |
| null | Cannot predict (before model creation or no active campaign) |
import com.monetai.sdk.MonetaiSDKJava;
import com.monetai.sdk.models.PredictResult;
private void predictUserPurchase() {
MonetaiSDKJava.getShared().predict((result, error) -> {
if (error != null) {
Log.e("MonetaiSDK", "Prediction failed: " + error.getMessage());
return;
}
if (result != null) {
Log.d("MonetaiSDK", "Prediction result: " + result.getPrediction());
Log.d("MonetaiSDK", "Test group: " + result.getTestGroup());
if (result.getPrediction() != null) {
PredictResult prediction = result.getPrediction();
if (prediction == PredictResult.NON_PURCHASER) {
// When predicted as non-purchaser, offer discount
Log.d("MonetaiSDK", "Predicted as non-purchaser - discount can be applied");
} else if (prediction == PredictResult.PURCHASER) {
// When predicted as purchaser
Log.d("MonetaiSDK", "Predicted as purchaser - discount not needed");
}
} else {
// Cannot predict
Log.d("MonetaiSDK", "Cannot predict - no active campaign or model");
}
}
});
}
API Reference
Return Value
| Return Value | Type | Description |
|---|---|---|
| prediction | PredictResult | Prediction result |
| testGroup | ABTestGroup | A/B test group |
PredictResult Type
| Value | Description |
|---|---|
| NON_PURCHASER | Predicted not to purchase |
| PURCHASER | Predicted to purchase |
| null | Cannot predict (before model creation or no active campaign) |
predict() Function- Multiple Calls
- When a promotion is already active from a previous
predict()call, callingpredict()multiple times will not start a new promotion. Only the same promotion information will be returned inonDiscountInfoChange.
- When a promotion is already active from a previous
- Restarting Promotions
- After a promotion period ends, calling
predict()again will start a new promotion if the user is still predicted as a non-paying user.
- After a promotion period ends, calling
- Handling Existing Subscribers
- If you do not want to provide promotions to users who are already on paid subscriptions, do not call the
predict()function for those users.
- If you do not want to provide promotions to users who are already on paid subscriptions, do not call the
2. Displaying Promotion UIβ
When predict() identifies a user as a non-paying user, the SDK delivers discount information via the onDiscountInfoChange callback. You can use this to display promotional UI to the user.
A. Quick Start with UI Templates (Recommended)β
Pass paywallConfig to MonetaiSDK.shared.configurePaywall(). When predict() returns a non-purchaser and the discount is within its valid period, the banner/paywall will appear automatically.
Paywall UI templates are supported in Android SDK 1.1.0 and above.
See available template styles and examples: UI Template Design Guide
- Kotlin
- Java
import com.monetai.sdk.MonetaiSDK
import com.monetai.sdk.models.PaywallConfig
import com.monetai.sdk.models.Feature
import com.monetai.sdk.models.PaywallStyle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Configure paywall
configurePaywall()
// Set subscription status (set initial value based on the user's actual subscription status)
MonetaiSDK.shared.setSubscriptionStatus(false)
}
private fun configurePaywall() {
val features = listOf(
Feature("All premium features", "Unlock everything", true),
Feature("Advanced analytics", "Insights and detailed reports"),
Feature("Priority support", "24/7 customer support")
)
val config = PaywallConfig(
discountPercent = 50,
regularPrice = "$10.00",
discountedPrice = "$5.00",
locale = "en",
style = PaywallStyle.HIGHLIGHT_BENEFITS,
features = features,
enabled = true,
bannerBottom = 20f
).apply {
onPurchase = { paywallContext, closePaywall ->
// TODO: Trigger your purchase flow
// On success, close paywall and update subscriber state
closePaywall()
MonetaiSDK.shared.setSubscriptionStatus(true)
}
onTermsOfService = { paywallContext ->
// TODO: Open Terms of Service
}
onPrivacyPolicy = { paywallContext ->
// TODO: Open Privacy Policy
}
}
MonetaiSDK.shared.configurePaywall(config)
}
}
import com.monetai.sdk.MonetaiSDKJava;
import com.monetai.sdk.models.PaywallConfig;
import com.monetai.sdk.models.Feature;
import com.monetai.sdk.models.PaywallStyle;
import com.monetai.sdk.models.PaywallContext;
import java.util.Arrays;
import java.util.List;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Configure paywall
configurePaywall();
// Set subscription status (set initial value based on the user's actual subscription status)
MonetaiSDKJava.getShared().setSubscriptionStatus(false);
}
private void configurePaywall() {
PaywallConfig config = MonetaiSDKJava.createPaywallConfigBuilder()
.discountPercent(50)
.regularPrice("$10.00")
.discountedPrice("$5.00")
.locale("en")
.style(PaywallStyle.HIGHLIGHT_BENEFITS)
.features(Arrays.asList(
new Feature("All premium features", "Unlock everything", true),
new Feature("Advanced analytics", "Insights and detailed reports", false),
new Feature("Priority support", "24/7 customer support", false)
))
.enabled(true)
.bannerBottom(20f)
.onPurchase((PaywallContext paywallContext, Runnable closePaywall) -> {
// TODO: Trigger your purchase flow
// On success, close paywall and update subscriber state
closePaywall.run();
MonetaiSDKJava.getShared().setSubscriptionStatus(true);
})
.onTermsOfService((PaywallContext paywallContext) -> {
// TODO: Open Terms of Service
})
.onPrivacyPolicy((PaywallContext paywallContext) -> {
// TODO: Open Privacy Policy
})
.build();
MonetaiSDKJava.getShared().configurePaywall(config);
}
}
PaywallConfig
- Kotlin
- Java
| Key | Type | Required | Description |
|---|---|---|---|
| discountPercent | Int | β | Discount percentage (0-100) |
| regularPrice | String | β | Regular price label |
| discountedPrice | String | β | Discounted price label |
| locale | String | β | Language code (e.g., "en", "ko") |
| style | PaywallStyle | β | Template style |
| features | List<Feature> | - | Only needed for .HIGHLIGHT_BENEFITS or .KEY_FEATURE_SUMMARY |
| enabled | Boolean | - | Enable automatic banner/paywall display (default: true) |
| bannerBottom | Float | - | Bottom offset for the floating banner (default: 20f) |
| Key | Type | Required | Description |
|---|---|---|---|
| discountPercent | int | β | Discount percentage (0-100) |
| regularPrice | String | β | Regular price label |
| discountedPrice | String | β | Discounted price label |
| locale | String | β | Language code (e.g., "en", "ko") |
| style | PaywallStyle | β | Template style |
| features | List<Feature> | - | Only needed for .HIGHLIGHT_BENEFITS or .KEY_FEATURE_SUMMARY |
| enabled | boolean | - | Enable automatic banner/paywall display (default: true) |
| bannerBottom | float | - | Bottom offset for the floating banner (default: 20f) |
PaywallStyle Types
- Kotlin
- Java
| Value | Description |
|---|---|
.COMPACT | Compact design |
.HIGHLIGHT_BENEFITS | Highlight benefits design |
.KEY_FEATURE_SUMMARY | Key feature summary design |
.TEXT_FOCUSED | Text-focused design |
| Value | Description |
|---|---|
PaywallStyle.COMPACT | Compact design |
PaywallStyle.HIGHLIGHT_BENEFITS | Highlight benefits design |
PaywallStyle.KEY_FEATURE_SUMMARY | Key feature summary design |
PaywallStyle.TEXT_FOCUSED | Text-focused design |
Feature Model
| Property | Type | Description |
|---|---|---|
| title | String | Feature title |
| description | String | Feature description |
| isPremiumOnly | Boolean | Whether feature is premium only (default: false) |
Callbacks
| Callback | Type | Description |
|---|---|---|
| onPurchase | (PaywallContext, (() -> Unit) -> Unit)? | Purchase button callback (call closePaywall() on success) |
| onTermsOfService | (PaywallContext -> Unit)? | Terms of Service click callback |
| onPrivacyPolicy | (PaywallContext -> Unit)? | Privacy Policy click callback |
PaywallContext
The PaywallContext provides access to the current activity and application context for UI operations:
| Property | Type | Description |
|---|---|---|
| activity | Activity | Current activity for UI operations |
| applicationContext | Context | Application context for resources |
Subscription Status Management
The SDK automatically controls banner/paywall visibility based on the user's subscription status. Use these methods to manage the subscription state:
- Kotlin
- Java
// Set initial subscription status (can be called before or after configurePaywall)
MonetaiSDK.shared.setSubscriptionStatus(true) // true for subscribers, false for non-subscribers
// Set initial subscription status (can be called before or after configurePaywall)
MonetaiSDKJava.getShared().setSubscriptionStatus(true); // true for subscribers, false for non-subscribers
Automatic Banner Control
When paywall is configured, the SDK automatically:
- Shows the banner when discount is active, paywall is enabled, and user is not a subscriber
- Hides the banner when user is a subscriber, no discount exists, discount has expired, or paywall is disabled
B. Custom UI (Advanced)β
B-1. Implementation using onDiscountInfoChange callback
You can build your own promotion screen. The Monetai SDK provides the discount timing (startedAt and endedAt) and triggers the onDiscountInfoChange callback. Your app can use this information to implement the custom UI components.
The startedAt and endedAt values in the discount information indicate the valid period for the promotion. You should use these timestamps to:
- Show your promotion screen only during the valid period
- Hide the screen when the period expires
Important: The onDiscountInfoChange callback must be registered before calling initialize().
Registering the callback later may cause you to miss existing active promotion information. This can lead to issues where previously activated promotions cannot be displayed immediately when the app restarts.
The onDiscountInfoChange callback is triggered both when the app starts and there's valid discount information and when a new discount is generated by a predict() call. Use it to update banners, prices, or other UI elements in real-time.
- Kotlin
- Java
import com.monetai.sdk.MonetaiSDK
import com.monetai.sdk.models.AppUserDiscount
import java.util.Date
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Set up discount info change callback BEFORE initialize()
MonetaiSDK.shared.onDiscountInfoChange = { discountInfo ->
runOnUiThread {
handleDiscountInfoChange(discountInfo)
}
}
// Initialize SDK after setting up callback
MonetaiSDK.shared.initialize(
context = this@MainActivity,
sdkKey = "YOUR_SDK_KEY",
userId = "USER_ID"
) { result, error ->
// Handle initialization result
}
}
private fun handleDiscountInfoChange(discountInfo: AppUserDiscount?) {
if (discountInfo != null) {
// Update UI when discount info is available
val now = Date()
val endTime = discountInfo.endedAt
if (now < endTime) {
// When discount is valid
showDiscountBanner(discountInfo)
}
} else {
// When no discount info
hideDiscountBanner()
}
}
private fun showDiscountBanner(discount: AppUserDiscount) {
// Show discount banner logic
println("Show discount banner: $discount")
}
private fun hideDiscountBanner() {
// Hide discount banner logic
println("Hide discount banner")
}
}
API Reference
Callback Type
public var onDiscountInfoChange: ((AppUserDiscount?) -> Unit)?
AppUserDiscount Type
| Property | Type | Description |
|---|---|---|
| id | number | Discount ID |
| startedAt | Date | Discount start time |
| endedAt | Date | Discount end time |
| appUserId | string | User ID |
| sdkKey | string | SDK key |
| createdAt | Date | Creation time |
import com.monetai.sdk.MonetaiSDKJava;
import com.monetai.sdk.models.AppUserDiscount;
import java.util.Date;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Set up discount info change callback BEFORE initialize()
MonetaiSDKJava.getShared().setOnDiscountInfoChange(discountInfo -> {
runOnUiThread(() -> {
handleDiscountInfoChange(discountInfo);
});
});
// Initialize SDK after setting up callback
MonetaiSDKJava.getShared().initialize(
this,
"YOUR_SDK_KEY",
"USER_ID",
(result, error) -> {
// Handle initialization result
}
);
}
private void handleDiscountInfoChange(AppUserDiscount discountInfo) {
if (discountInfo != null) {
// Update UI when discount info is available
Date now = new Date();
Date endTime = discountInfo.endedAt;
if (now.before(endTime)) {
// When discount is valid
showDiscountBanner(discountInfo);
}
} else {
// When no discount info
hideDiscountBanner();
}
}
private void showDiscountBanner(AppUserDiscount discount) {
// Show discount banner logic
Log.d("MonetaiSDK", "Show discount banner: " + discount);
}
private void hideDiscountBanner() {
// Hide discount banner logic
Log.d("MonetaiSDK", "Hide discount banner");
}
}
API Reference
Callback Type
public void setOnDiscountInfoChange(callback: ((AppUserDiscount?) -> Unit)?)
AppUserDiscount Type
| Property | Type | Description |
|---|---|---|
| id | number | Discount ID |
| startedAt | Date | Discount start time |
| endedAt | Date | Discount end time |
| appUserId | string | User ID |
| sdkKey | string | SDK key |
| createdAt | Date | Creation time |
Managing User Stateβ
SDK Resetβ
Initialize the SDK when the user logs out.
- Kotlin
- Java
import com.monetai.sdk.MonetaiSDK
// When user logs out
fun logoutUser() {
MonetaiSDK.shared.reset()
println("User logout completed")
}
import com.monetai.sdk.MonetaiSDKJava;
// When user logs out
private void logoutUser() {
MonetaiSDKJava.getShared().reset();
Log.d("MonetaiSDK", "User logout completed");
}
Support
If you run into any trouble during the integration, don't hesitate to contact us at support@monetai.io.
Next Stepsβ
With the SDK setup complete, you're ready to create a model and launch a campaign: