Android SDK
Native Kotlin SDK for real-time push notifications with no Firebase dependency, rich notifications, in-app messages, message inbox, and VoIP support.
A persistent connection over TLS (port 443), no Firebase dependency (FCM bridge is optional)
Notifications delivered even when the app is killed or the device restarts
Images, action buttons, deep links, custom sounds
Modal, banner, fullscreen, and card layouts
Persistent storage with real-time updates and unread counts
Subscribe users to topics or target by segment
Variant assignment with delivery analytics
Full control over your push infrastructure; optional FCM bridge
Installation#
AbrovaPush installs from Abrova Center in two steps.
- Add the Abrova Center repository to
settings.gradle.kts:
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven { url = uri("https://center.abrova.ir/api/packages/abrova/maven") }
}
}Groovy: maven { url "https://center.abrova.ir/api/packages/abrova/maven" }
- Add the dependency to your app's
build.gradle.kts, then sync the project with Gradle files:
dependencies {
implementation("ir.abrova:abrova-push-android:0.1.15")
}Required Permissions
The SDK requires these permissions, automatically merged via the library manifest:
<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
Quick Start#
Initialize AbrovaPush in your Application class:
import android.app.Application
import ir.abrova.push.sdk.AbrovaPush
import ir.abrova.push.sdk.AbrovaPushConfig
class MyApplication : Application() {
override fun onCreate() {
super.onCreate()
val config = AbrovaPushConfig(
apiKey = "ab_live_your_api_key"
)
AbrovaPush.init(this, config)
}
}Don't forget to register the Application class in your AndroidManifest.xml:
<application
android:name=".MyApplication"
...>Register and Receive Messages#
import android.os.Build
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import ir.abrova.push.sdk.AbrovaPush
import ir.abrova.push.sdk.AbrovaPushCallbackAdapter
import ir.abrova.push.sdk.AbrovaPushMessage
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Request notification permission on Android 13+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
requestPermissions(arrayOf(android.Manifest.permission.POST_NOTIFICATIONS), 1)
}
// Listen for messages and connection events
AbrovaPush.setCallback(object : AbrovaPushCallbackAdapter() {
override fun onMessageReceived(message: AbrovaPushMessage) {
// New push notification arrived
}
override fun onConnectionStateChanged(connected: Boolean) {
// Real-time connection state changed
}
override fun onRegistered(deviceId: String) {
// Device registered with the Abrova backend
}
override fun onError(error: String) {
// Connection or registration error
}
})
// Register on every launch: it keeps the device's token, app version
// and last-seen time current on the server
AbrovaPush.register(
userId = "user_123",
metadata = mapOf(
"tier" to "premium",
"language" to "en"
)
)
}
}Handle Notification Taps#
When the user taps a push notification and the app launches from the cold-start, retrieve the initial message:
val initialMessage = AbrovaPush.getInitialMessage()
if (initialMessage != null) {
// Navigate based on initialMessage.deepLink or message data
AbrovaPush.clearInitialMessage()
}User Identification#
// Set user ID after login
AbrovaPush.setUserId("user_123")
// Clear user ID after logout — also detaches the user on the server,
// so this device stops receiving the previous user's notifications
AbrovaPush.clearUserId()Topics#
Subscribe users to topics so you can broadcast notifications to interest groups:
AbrovaPush.subscribeTopic("news")
AbrovaPush.subscribeTopic("sports")
// Later, when the user opts out
AbrovaPush.unsubscribeTopic("news")Rich Notifications#
Rich notifications support images, action buttons, deep links, and custom sounds. These fields are populated from the dashboard or API when sending a push.
| Option | Type | Required | Description |
|---|---|---|---|
| imageUrl | String? | No | URL of a large image displayed in the expanded notification |
| actions | List<Action> | No | Up to 3 action buttons |
| sound | String? | No | Custom sound file name or "default" |
| deepLink | String? | No | Deep link URI launched when the notification is tapped |
| priority | String? | No | "default", "high" or "low" |
| ttl | Int? | No | Time-to-live in seconds before the notification expires |
In-App Messages#
In-app messages display modal, banner, fullscreen, or card layouts inside your app — useful for promotions, onboarding, and announcements.
The built-in UI draws on the Activity you pass to AbrovaPush.setCurrentActivity(this) (call it in onResume()), and runs only while no InAppMessageCallback is set. Setting a callback means you render messages yourself: the SDK records the impression, calls onMessageReady and shows nothing. onButtonClicked / onMessageDismissed come only from the built-in UI, so they do not fire while a callback is set.
import ir.abrova.push.sdk.inapp.InAppMessage
import ir.abrova.push.sdk.inapp.InAppButton
import ir.abrova.push.sdk.inapp.InAppMessageCallback
// Built-in UI: in your Activity's onResume()
AbrovaPush.setCurrentActivity(this)
// Optional, only to render messages yourself (disables the built-in UI)
AbrovaPush.setInAppMessageCallback(object : InAppMessageCallback {
override fun onMessageReady(message: InAppMessage) {
// Render the message with your own UI
}
override fun onButtonClicked(message: InAppMessage, button: InAppButton) {
// Built-in UI only
}
override fun onMessageDismissed(message: InAppMessage) {
// Built-in UI only
}
override fun onError(error: String) {
// Failed to fetch or display a message
}
})
// Trigger on app open
AbrovaPush.triggerInAppOnAppOpen()
// Trigger on custom event
AbrovaPush.triggerInAppEvent("viewed_product")Message Inbox#
Inbox messages are stored server-side (sent from Abrova Console or the Inbox API, separately from pushes) so users can revisit them, similar to a messaging app inbox.
import ir.abrova.push.sdk.inbox.InboxFilter
import ir.abrova.push.sdk.inbox.InboxCallback
import ir.abrova.push.sdk.inbox.InboxMessage
import ir.abrova.push.sdk.inbox.InboxMessageStatus
// Fetch inbox messages
AbrovaPush.getInboxMessages(
filter = InboxFilter(limit = 50),
onSuccess = { response ->
val messages = response.messages
val unread = response.unreadCount
},
onError = { error -> /* handle */ }
)
// Listen for real-time inbox updates
AbrovaPush.getInboxManager().setCallback(object : InboxCallback {
override fun onMessageReceived(message: InboxMessage) {
// New inbox message arrived
}
override fun onMessageStatusChanged(messageId: String, status: InboxMessageStatus) {
// Read / unread / archived state changed
}
})
// Unread count for badges
val count = AbrovaPush.getInboxManager().getUnreadCount()Configuration Options#
val config = AbrovaPushConfig(
apiKey = "ab_live_your_api_key",
notificationIcon = "ic_notification",
showServiceNotification = true,
showNotificationInForeground = true,
autoRefresh = true
)| Option | Type | Required | Description |
|---|---|---|---|
| apiKey | String | Yes | Your project API Key from the dashboard |
| notificationIcon | String? | No | Resource name of the small notification icon |
| showServiceNotification | Boolean | No | Show the persistent foreground-service notification (default: true) |
| showNotificationInForeground | Boolean | No | Show notifications when the app is in foreground (default: true) |
| autoRefresh | Boolean | No | On init(), silently refresh a previously registered device at most once every 24h, or sooner when the app version, SDK version or user changed (default: true) |
The SDK never prompts for notification permission during a refresh. The installed SDK version is available as AbrovaPush.SDK_VERSION.
Delivery Tracking
Every notification the device receives is confirmed to Abrova Push, so the Delivery page in the Abrova Console shows it as delivered, not just sent. No setup is needed. See Delivery Receipts.
VoIP Calls (Optional)#
For incoming-call UI on Android, add the companion VoIP SDK. The Push SDK works on its own — VoIP is only needed if your app has real calling features.
dependencies {
implementation("ir.abrova:abrova-push-android:0.1.15")
implementation("ir.abrova:abrova-push-voip:0.1.0") // Optional
}import ir.abrova.push.voip.AbrovaPushVoip
import ir.abrova.push.voip.VoipConfig
import ir.abrova.push.voip.VoipCallback
import ir.abrova.push.voip.CallData
AbrovaPushVoip.initialize(this, VoipConfig(appName = "MyApp"))
AbrovaPushVoip.setCallback(object : VoipCallback {
override fun onCallAccepted(callData: CallData) {
// Connect to your calling service (Jitsi, WebRTC, etc.)
}
override fun onCallDeclined(callData: CallData) { }
override fun onCallTimeout(callData: CallData) { }
})Send a push payload with {"type": "voip_call", "callerName": "John"} to trigger the incoming call screen automatically.
API Reference#
Full method surface of AbrovaPush and supporting classes.
AbrovaPush — Initialization & Lifecycle
| Option | Type | Required | Description |
|---|---|---|---|
| init(context, config) | - | - | Initialize the SDK with an AbrovaPushConfig |
| register(userId?, metadata?) | - | - | Register the device with the Abrova backend. Call on every launch |
| setCallback(callback) | - | - | Set the message and connection callback |
| getInitialMessage() | - | - | Get the message that launched the app, if any |
| clearInitialMessage() | - | - | Clear the cached initial message after handling |
User & Topics
| Option | Type | Required | Description |
|---|---|---|---|
| setUserId(id) | - | - | Set the current user identifier |
| clearUserId() | - | - | Clear the user identifier and detach the user on the server (e.g. on logout) |
| subscribeTopic(topic) | - | - | Subscribe the device to a topic |
| unsubscribeTopic(topic) | - | - | Unsubscribe from a topic |
In-App Messages
| Option | Type | Required | Description |
|---|---|---|---|
| setCurrentActivity(activity) | - | - | Activity the built-in in-app UI draws on (call in onResume) |
| setInAppMessageCallback(callback) | - | - | Render in-app messages yourself (turns off the built-in UI) |
| triggerInAppOnAppOpen() | - | - | Trigger any in-app message registered for app-open |
| triggerInAppEvent(eventName, properties?) | - | - | Trigger in-app messages registered for a custom event |
Inbox
| Option | Type | Required | Description |
|---|---|---|---|
| getInboxMessages(filter, onSuccess, onError) | - | - | Fetch a page of inbox messages |
| getInboxManager() | - | - | Get the InboxManager for real-time updates and unread counts |
Device Compatibility#
- Android 5.0 Lollipop — API 21 (minimum supported)
- Android 8.0 Oreo — API 26 (notification channels)
- Android 13 Tiramisu — API 33 (runtime POST_NOTIFICATIONS permission)
- Android 14 — API 34
Best Practices#
Call AbrovaPush.init() in your Application onCreate() so the SDK is ready before any Activity
POST_NOTIFICATIONS is a runtime permission — request it the first time the user lands on a screen that benefits from notifications
Call setUserId() once authentication completes so notifications target the right account across devices
Topics suit large audiences; use them for newsletters, news categories, and broadcast announcements
Segments evaluate user metadata server-side — better for targeted product launches and re-engagement
Call getInitialMessage() once on cold start, navigate, then call clearInitialMessage() so it isn't replayed on resume