Android Integration
This guide shows how to add the AlgorithmX SDK to an Android app that is already in production, with its own login, analytics, Firebase push notifications, and navigation. You do not rebuild any of these. You add a few calls in the places where your app already handles startup, sign-in, business events, notifications, and navigation. The SDK does the rest: it shows campaign notifications and in-app campaigns, tracks interactions, and applies frequency rules.
The SDK entry point is algorithmx.engage.core.AlgorithmX. Names such as appNavigator, notificationClickListener, and actionButtonHandler in the examples stand for your own code; they are not SDK APIs.
How the SDK fits into your app
| Where in your existing app | What you add | Section |
|---|---|---|
| Gradle build | The SDK dependency. | 2 |
| Application.onCreate | Initialize the SDK and register your handlers. | 3 |
| Login, restored session, logout | Tell the SDK who the customer is. | 4 |
| Business events (product viewed, order confirmed, and so on) | Forward the agreed events. | 5 |
| FirebaseMessagingService and launcher Activity | Pass the FCM token and AlgorithmX messages to the SDK. | 6 |
| Deep-link router | Let campaign links open your existing screens. | 7 |
| Business actions and screens | Handle campaign custom actions and notification buttons. | 8 |
In-app campaigns need no screen or view code from you: once these connections are in place, the SDK shows campaigns by itself (section 9).
1. Before you start
What AlgorithmX gives you
- The SDK as a release AAR with its version, or as a source module.
- The API base URL for each environment, such as staging and production. All URLs use HTTPS.
What your team gives AlgorithmX
AlgorithmX sends notifications to your app through your existing Firebase project, so you keep your current Firebase setup and give AlgorithmX permission to send through it:
| Item | Example | Where to find it |
|---|---|---|
| Application ID of each app build | com.example.shop | applicationId in your app's build.gradle. |
| Firebase project your app uses | shop-prod-1234 | Firebase console, Project settings, or your app's google-services.json. |
| Access to send messages for that project | A service account key allowed to send Firebase Cloud Messaging messages, or another access method agreed with AlgorithmX | Google Cloud console for the Firebase project. Send it through the agreed secure channel; never include it in the app. |
Also agree with the AlgorithmX team on the customer ID you will use (section 4), the events you will send (section 5), and the deep links and custom actions campaigns may use (sections 7 and 8).
If your app does not use Firebase Cloud Messaging yet, set it up first with Firebase's Android guide, then continue here.
2. Add the SDK
The SDK needs minSdk 24, compileSdk 34 or higher, and Java 17. It does not include Firebase; your app keeps its own Firebase dependencies.
Delivered AAR: add the file to your app module. The file name is an example; use the delivered one.
// Add to the existing app/build.gradle dependencies block.
implementation files('libs/algorithmx-android-sdk.aar')An AAR file does not bring its own dependencies, so add any of these your app does not already have. Keep newer versions your app already uses.
// SDK runtime dependencies; reconcile with your existing dependency catalog.
implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.google.code.gson:gson:2.11.0'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1'
implementation 'androidx.sqlite:sqlite-ktx:2.4.0'Source module (if delivered instead): include it in settings.gradle and add implementation project(':android-sdk'). The module brings its dependencies with it.
Release builds: if you shrink your release build and the delivered AAR does not include the SDK's consumer rules, add this rule so in-app campaigns keep working:
-keepclassmembers class * {
@android.webkit.JavascriptInterface <methods>;
}The SDK adds its own screens and receivers to your merged manifest, plus the INTERNET permission. You do not declare them yourself.
3. Initialize the SDK in your Application
Add these lines to your existing Application.onCreate. Keep your current startup code as it is.
// Add inside the current Application's startup path.
AlgorithmX.initialize(this, "https://api.example.com")
AlgorithmX.setLoggingEnabled(BuildConfig.DEBUG)
AlgorithmX.setSmallIcon(R.drawable.ic_stat_notification) // Reuse your current icon.
// Register application-owned adapters implemented in sections 7 and 8.
AlgorithmX.setDeepLinkHandler(deepLinkHandler)
AlgorithmX.setNotificationClickListener(notificationClickListener)
AlgorithmX.setActionButtonHandler(actionButtonHandler)- Initialize in the Application, not in an Activity. Firebase can start your app's process in the background to deliver a message; no Activity opens then, but
Application.onCreatestill runs. Calls made beforeinitializeare ignored. - Reuse your notification icon with
setSmallIcon. Without it, AlgorithmX notifications show a generic Android icon. - Keep handlers application-scoped. The SDK keeps the objects you register for the life of the process, so they should call into your navigation layer rather than hold an Activity.
4. Identify your customers
Until you identify a customer, the SDK uses the device's Android ID. Call identifyUser with the same customer ID your backend uses, in two places: after a successful login, and when your app restores a saved session at startup. The second one matters for customers who are already signed in when they update to the first release with AlgorithmX.
// In the existing restored-session or successful-login hook.
AlgorithmX.identifyUser(
"customer_123",
mapOf("first_name" to "Amina", "language" to "ar", "loyalty_tier" to "gold")
)The SDK remembers the customer across app restarts and sends their previous anonymous ID, so AlgorithmX can link earlier activity to the profile. At logout, and when one account replaces another, clear waiting campaigns so the next person does not see them:
// Before completing logout:
AlgorithmX.clearWebViewQueue()
AlgorithmX.resetIdentity()
// Before replacing one signed-in account with another:
AlgorithmX.clearWebViewQueue()
AlgorithmX.identifyUser("customer_456", mapOf("language" to "en"))resetIdentity()returns to the anonymous device ID. It does not close a campaign that is on screen or remove notifications already shown; handle those in your logout flow if needed.- AlgorithmX identity follows your login; it does not replace your authentication.
5. Track business events
Forward the agreed events from the places where your app already knows an action succeeded, for example your order-confirmation handler, next to your existing analytics calls.
AlgorithmX.trackEvent("product_view", mapOf("product_id" to "SKU-123"))
AlgorithmX.trackEvent("add_to_cart", mapOf("product_id" to "SKU-123", "quantity" to 2))
AlgorithmX.trackEvent("purchase", mapOf(
"order_id" to "ORDER-1001",
"total" to 149.90,
"currency" to "SAR",
"items" to listOf(mapOf("product_id" to "SKU-123", "quantity" to 2))
))- Use JSON-friendly values: strings, numbers, booleans, and lists or maps of them.
- Use simple event names such as
add_to_cart; the name becomes part of the request URL. - Events are not retried. An event sent while the device is offline is lost, so keep your own backend as the source of truth for orders and payments.
- No history is imported. Past activity reaches AlgorithmX only through a separate data import agreed with the AlgorithmX team.
6. Connect your existing Firebase messaging
Your app keeps its current Firebase project and messaging service. You add three things: pass the token to AlgorithmX, pass AlgorithmX messages to the SDK, and forward new Intents from your launcher Activity as a safeguard.
Pass the FCM token
Register the current token on every launch, for example in Application.onCreate after initialization:
// Reuse this token in both your existing registration and AlgorithmX registration.
FirebaseMessaging.getInstance().token.addOnSuccessListener { token ->
AlgorithmX.registerDeviceToken(token)
// Keep your existing token registration here or in its current owner.
}Customers who update your app already have a token that AlgorithmX has never seen, and Firebase does not call onNewToken just because you added the SDK. If your code skips registration when the token is unchanged, do not skip the AlgorithmX call.
Forward AlgorithmX messages from your messaging service
Add an AlgorithmX branch at the start of your existing FirebaseMessagingService. AlgorithmX messages go to the SDK and stop there; everything else continues through your current code.
// Inside the existing FirebaseMessagingService; do not create a second service.
override fun onMessageReceived(message: RemoteMessage) {
super.onMessageReceived(message)
// Keep shared receipt/logging hooks here, if present.
if (AlgorithmX.isAlgorithmXPush(message.data)) {
AlgorithmX.handleFcmMessage(
applicationContext,
message.data,
message.notification?.title,
message.notification?.body
)
return // Do not also render this message in the existing notification path.
}
// Keep your current non-AlgorithmX message routing/rendering here.
}
override fun onNewToken(token: String) {
super.onNewToken(token)
// Keep current backend/provider token registrations here.
AlgorithmX.registerDeviceToken(token)
}- The branch must come before your own notification code. The SDK builds AlgorithmX notifications itself, with image and buttons, and saves in-app campaign messages without showing anything. If your code also handles these messages, customers see duplicates or stray notifications.
- The SDK posts its notifications on its own channel,
algorithmx_notifications, using your app's notification permission. Keep your existing permission prompt; the SDK never asks for permission. - AlgorithmX notification IDs start at 10000. If your app also uses IDs in that range, tell the AlgorithmX team.
Forward new Intents to the SDK
The SDK routes taps on its own notifications by itself, whatever state the app is in. As a safeguard, add one line to the existing onNewIntent of your launcher Activity. It lets the SDK route AlgorithmX data that arrives through an Intent to an Activity that is already open, for example from a notification that the system displayed instead of the SDK:
// In the existing Activity that receives notification launch Intents
override fun onNewIntent(intent: android.content.Intent) {
super.onNewIntent(intent)
setIntent(intent)
AlgorithmX.handleNewIntent(this, intent)
// Keep current handling for other notification/deep-link Intents.
}7. Connect deep links to your router
When a campaign's action is "open screen", the SDK passes the campaign's link to your DeepLinkHandler. Connect it to the router your app already uses for links:
val deepLinkHandler = object : algorithmx.engage.interfaces.DeepLinkHandler {
override fun onDeepLinkReceived(uri: android.net.Uri): Boolean {
return appNavigator.openDeepLink(uri)
}
}
AlgorithmX.setDeepLinkHandler(deepLinkHandler)- Your router does the navigation. The SDK never opens the link itself, and a manifest intent filter alone does not connect campaigns to your router. If no handler is set, nothing happens.
- Return true when your router accepts the link, including when it waits for login first, and false when it rejects it.
- The handler can run before any of your own Activities is open, for example after a tap that starts the app. Start screens with
FLAG_ACTIVITY_NEW_TASK, or let your router hold the destination until your UI is ready. - Reuse your existing links. Because the SDK hands the link straight to your router, you do not need new intent filters, App Links, or new screens. Give the campaign team the list of links your app supports, with their required parameters, for example
myapp://open/product?id=SKU-123orhttps://shop.example.com/cart.
8. Handle custom actions and notification buttons
Each campaign notification has one main action, chosen by the campaign team:
| Campaign action | What happens | Your code |
|---|---|---|
| Open screen | The link goes to your DeepLinkHandler. | Section 7. |
| Custom action | The SDK calls onCustomAction with the action name and its parameters. | Map each agreed action name to your screen or flow. |
| Open web page | The SDK opens the URL in the browser. | None. |
| Open in-app campaign | The SDK shows the campaign page over your app. | None. |
Map custom actions to your existing navigation:
val notificationClickListener = object : algorithmx.engage.interfaces.NotificationClickListener {
override fun onNotificationClick(data: Map<String, Any>): Boolean = false
override fun onCustomAction(action: String?, data: Map<String, Any>): Boolean {
val parsed = data["parsedActionData"] as? Map<*, *> ?: return false
return when (action) {
"view_product" -> {
val productId = (parsed["product_id"] as? String)
?.takeIf { it.isNotBlank() } ?: return false
appNavigator.openProduct(productId)
}
"view_cart" -> appNavigator.openCart()
else -> false
}
}
}
AlgorithmX.setNotificationClickListener(notificationClickListener)onNotificationClickruns first for every tap. Return false to let the SDK run the campaign's action; return true only if your own code fully handled the tap.- The action's parameters are in
parsedActionData. Agree on the action names and parameter types with the campaign team: a numeric value arrives as a number, soas? Stringfails for it. - An action name your app does not know does nothing, so only offer campaign actions that your released app version implements.
Notifications can show up to three buttons, which have their own handler. Match on the button's action text, not on its translated title:
val actionButtonHandler = object : algorithmx.engage.interfaces.ActionButtonHandler {
override fun onActionButtonClicked(
buttonId: String,
actionText: String,
title: String,
notificationData: Map<String, Any>
): Boolean = when (actionText) {
"view_cart" -> appNavigator.openCart()
"dismiss" -> true // Prevent the notification's main-action fallback.
else -> false
}
}
AlgorithmX.setActionButtonHandler(actionButtonHandler)Every button opens the app. Return true when you handled the button. If you return false, the SDK runs the notification's main action instead.
A campaign parameter is only a navigation hint: keep your normal login and permission checks before sensitive actions.
9. In-app campaigns
In-app campaigns need no extra code. With sections 3 to 6 in place:
- AlgorithmX sends a data message to the device, and your messaging service passes it to the SDK.
- The SDK saves the campaign without showing a notification.
- The next time the customer opens or returns to the app, the SDK shows the campaign over the current screen, at most one per visit. It closes with the campaign's close button or the Back button.
Points to check in your app:
- Logout and account switch: clear waiting campaigns as shown in section 4.
- Your own pop-ups at launch: a campaign can appear at the moment the app opens, on top of whatever is on screen. Check the combination with any dialog your app shows at startup.
- Notification permission is not needed for in-app campaigns; they arrive even if the customer turned notifications off.
- Force-stopped apps: if the customer force-stops the app from system settings, Android delivers no messages until the app is opened again.
To follow campaign activity while testing, you can register AlgorithmX.setCampaignInteractionListener(...); it reports each impression, click, and close.
10. Test and ship
Test on a real device with a test customer, using both a debug build and your minified release build.
| Check | Expected result |
|---|---|
| Update from your current Play Store version while signed in | The customer is identified without logging in again, and the token is registered. |
| Login, restart, logout, second account | Events use the right customer each time, and no campaign from the first account appears for the second. |
| Your existing notifications | Other notifications behave exactly as before, and AlgorithmX notifications appear once. |
| AlgorithmX notification with the app open, in the background, and closed | A tap opens the right screen once, including when the app was already open on another screen. |
| Each deep link and custom action your app supports | The right screen opens; a missing parameter or unknown link follows your app's normal fallback. |
| Each notification button | The button's action runs, or the main action when your handler returns false. |
| In-app campaign | It appears the next time the app comes to the foreground, over the current screen, and closes with its close button or Back. |
| Notification permission denied | In-app campaigns still arrive; visible notifications do not. |
| Release build | Notifications, taps, and in-app campaign buttons still work after minification. |
| Business events | The agreed events appear for the customer in AlgorithmX. |
While testing, setLoggingEnabled(true) writes each SDK request to Logcat under the AlgorithmX tag.
API reference
| Call on AlgorithmX | Use |
|---|---|
initialize(application, apiBaseUrl) | Start the SDK in Application.onCreate. |
setLoggingEnabled(enabled) | Write SDK requests to Logcat while testing. Off by default. |
setSmallIcon(resId) | Use your notification icon for AlgorithmX notifications. |
identifyUser(userId, attributes) | Identify the customer after login and when a session is restored. |
resetIdentity() | Return to the anonymous device ID at logout. |
trackEvent(name, properties) | Send a business event. |
registerDeviceToken(token) | Pass the FCM token on every launch and in onNewToken. |
isAlgorithmXPush(data) | Check whether an FCM message comes from AlgorithmX. |
handleFcmMessage(context, data, title, body) | Forward AlgorithmX messages from onMessageReceived. |
handleNewIntent(activity, intent) | Forward new Intents from Activity.onNewIntent. |
setDeepLinkHandler, setNotificationClickListener, setActionButtonHandler, setCampaignInteractionListener | Register your handlers. Each has a matching remove call. |
clearWebViewQueue() | Remove waiting campaigns at logout or account switch. |
getDeviceFingerprint(), getQueueSize() | Inspect the current customer ID and waiting campaigns while testing. |
Troubleshooting
| Symptom | What to check |
|---|---|
| No events in AlgorithmX | initialize runs in Application.onCreate with the right HTTPS URL, and the device is online. |
| No AlgorithmX notifications arrive | The token is registered with AlgorithmX on every launch; AlgorithmX can send through your Firebase project; your service forwards AlgorithmX messages; notifications and the algorithmx_notifications channel are enabled. |
| Duplicate or missing notifications | The AlgorithmX branch runs before your own notification code and returns after forwarding; other messages still reach your code. |
| Tapping a notification does nothing | Your handlers are registered in Application.onCreate and actually start navigation. |
| Deep link does nothing | DeepLinkHandler is set and actually starts navigation; the link is one your router accepts. |
| Custom action is skipped | onNotificationClick returns false, and the action name matches exactly. |
| A button opens an unexpected screen | Your button handler returned false, so the main action ran. Check the action text. |
| In-app campaign never appears | Your service forwards the message, the app was not force-stopped, and you reopened the app after the campaign was sent. |
| In-app campaign buttons stop working in release | Add the keep rule from section 2. |
| A previous account's campaign appears | Call clearWebViewQueue before logout and account switch. |
For creating campaigns, continue with In-App Push Campaigns and In-App View Campaigns. Return to Integrations for the other platforms.
