NPNotify Partners Docs

Android SDK

Integrating Notify Partners push notifications into an Android application — installation via Maven Central, Firebase setup, SDK initialization, and full API reference.

Requirements

  • Android 5.0+ (API 21)
  • Kotlin 2.x+ or Java 8+
  • Firebase project with FCM (Firebase Cloud Messaging) configured
  • app_id from the Notify Partners dashboard

1. Installation

Gradle (Maven Central)

app/build.gradle.kts:

dependencies {
    implementation("partners.notify:notify-partners-sdk:1.0.0")
    implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
    implementation("com.google.firebase:firebase-messaging")
}

Maven Central is included by default in new Android projects. If needed, ensure mavenCentral() is in your settings.gradle.kts:

dependencyResolutionManagement {
    repositories {
        google()
        mavenCentral()
    }
}

2. Firebase Setup

  1. Create a project in the Firebase Console
  2. Add your Android application to the project
  3. Download google-services.json and place it in app/
  4. Add the Google Services plugin:

build.gradle.kts (root):

plugins {
    id("com.google.gms.google-services") version "4.4.0" apply false
}

app/build.gradle.kts:

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.gms.google-services")
}

3. SDK Initialization

Initialize the SDK in Application.onCreate() — this ensures the SDK is ready before any Activity starts.

Basic

import android.app.Application
import partners.notify.sdk.NotifyPartnersSDK

class YourApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        NotifyPartnersSDK.initialize(
            context = this,
            appId = "YOUR_APP_ID"
        )
    }
}

With Config Builder

For advanced configuration, use NotifyPartnersConfig.Builder:

import android.app.Application
import android.util.Log
import partners.notify.sdk.NotifyPartnersConfig
import partners.notify.sdk.NotifyPartnersListener
import partners.notify.sdk.NotifyPartnersSDK

class YourApplication : Application() {
    override fun onCreate() {
        super.onCreate()

        val config = NotifyPartnersConfig.Builder("YOUR_APP_ID")
            .externalId("user-123")
            .debugLogging(BuildConfig.DEBUG)
            .listener(object : NotifyPartnersListener {
                override fun onInitialized(instanceId: String) {
                    Log.d("SDK", "Ready: $instanceId")
                }
                override fun onInitializationFailed(error: Throwable) {
                    Log.e("SDK", "Init failed", error)
                }
                override fun onMessageReceived(
                    msgId: String, title: String, body: String, data: Map<String, String>
                ) {
                    Log.d("SDK", "Push received: $title")
                }
            })
            .build()

        NotifyPartnersSDK.initialize(this, config)
    }
}

Don't forget AndroidManifest.xml

Declare your Application class:

<application
    android:name=".YourApplication"
    ...>

What Happens During Initialization

  1. A unique device token is generated (or restored from SharedPreferences) and the device is registered on the server
  2. The FCM token is requested and device info is sent to the server
  3. Periodic updates are scheduled via WorkManager
  4. Lifecycle observer is registered (automatic onscreen/background events)
  5. Click interceptor is registered (automatic clicked events)

4. That's It — Everything is Automatic

The SDK automatically tracks lifecycle and handles notification clicks. No code in Activity is needed.

  • Lifecycle trackingProcessLifecycleOwner sends onscreen/background events automatically when the app moves between foreground and background.
  • Click handlingActivityLifecycleCallbacks intercepts notification taps and sends clicked events automatically. Duplicate clicks are prevented.

Your Activity can be empty:

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // Nothing else needed — the SDK handles everything
    }
}

If you need to handle the URL from a notification (e.g., for navigation):

import partners.notify.sdk.NotifyPartnersConstants

val url = intent?.getStringExtra(NotifyPartnersConstants.EXTRA_URL)

5. Push Notification Handling

The SDK automatically registers NotifyPartnersFirebaseService, which:

  1. Receives an FCM message — extracts msg_id from data
  2. Sends a delivered event — via NotifyPartnersSDK.notifyDelivered(msgId) (or direct API call if SDK is not yet initialized)
  3. Calls NotifyPartnersListener.onMessageReceived — if a listener is set
  4. Displays the notification — with title, body, optional image, and click handling
  5. On tap — opens the main Activity with NotifyPartnersConstants.EXTRA_MSG_ID and NotifyPartnersConstants.EXTRA_URL in Intent extras; the clicked event is sent automatically

Data payload format from the server:

{
  "msg_id": "message-uuid",
  "title": "Title",
  "body": "Notification body",
  "image": "https://example.com/image.png",
  "url": "https://example.com/action"
}

6. Tags and Segmentation

Set tags to segment users:

NotifyPartnersSDK.setTags(mapOf(
    "plan" to "premium",
    "city" to "moscow",
    "interests" to "sports"
))

Update the external ID after user login:

NotifyPartnersSDK.setExternalId("user-456")

Validation limits:

  • Maximum 50 tags per call
  • Tag key: maximum 128 characters
  • Tag value: maximum 256 characters
  • externalId: maximum 256 characters
  • Violations throw IllegalArgumentException

Persistence: tags and externalId are saved to SharedPreferences and survive process death.

Debouncing: rapid consecutive calls to setTags or setExternalId are coalesced — only the last value is sent to the server.

7. Permissions

The SDK automatically declares the required permissions in its AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

For Android 13+ (API 33), you must request the POST_NOTIFICATIONS permission at runtime:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestPermissions(arrayOf(Manifest.permission.POST_NOTIFICATIONS), 1)
}

API Reference

NotifyPartnersSDK

Kotlin object (singleton). The main entry point for working with the SDK.

object NotifyPartnersSDK

initialize(context, config)

Initialize the SDK with a NotifyPartnersConfig. Call in Application.onCreate().

@JvmStatic
@Synchronized
fun initialize(context: Context, config: NotifyPartnersConfig)

Parameters:

ParameterTypeRequiredDescription
contextContextYesApplication context
configNotifyPartnersConfigYesSDK configuration (see below)

Protected against double initialization — subsequent calls are ignored.


initialize(context, appId, externalId?)

Convenience overload for simple initialization.

@JvmStatic
@Synchronized
fun initialize(
    context: Context,
    appId: String,
    externalId: String? = null
)

Parameters:

ParameterTypeRequiredDescription
contextContextYesApplication context
appIdStringYesApplication ID from the Notify Partners dashboard
externalIdString?NoExternal user identifier

setExternalId

Update the external user identifier.

@JvmStatic
fun setExternalId(externalId: String)
ParameterTypeRequiredDescription
externalIdStringYesNew external identifier (max 256 characters)
  • Throws IllegalArgumentException if exceeds 256 characters
  • If SDK is not yet initialized, the call is silently ignored (validation still runs)
  • Saved to SharedPreferences (persists across restarts)
  • Debounced — rapid calls are coalesced

setTags

Set tags for user segmentation.

@JvmStatic
fun setTags(tags: Map<String, String>)
ParameterTypeRequiredDescription
tagsMap<String, String>YesTags for segmentation (max 50 tags, key max 128, value max 256 chars)
  • Throws IllegalArgumentException on validation failure
  • If SDK is not yet initialized, the call is silently ignored (validation still runs)
  • Tags are replaced in full (not merged)
  • Saved to SharedPreferences (persists across restarts)
  • Debounced — rapid calls are coalesced

notifyDelivered

Report that a push notification was delivered. Called automatically by NotifyPartnersFirebaseService.

@JvmStatic
fun notifyDelivered(msgId: String)
  • Retry policy: EVENT (2 attempts, backoff from 500ms)
  • If SDK is not initialized, the call is skipped (a warning is logged)

notifyClicked

Report that a push notification was clicked. Called automatically by the click interceptor.

@JvmStatic
fun notifyClicked(msgId: String)
  • Retry policy: EVENT (2 attempts, backoff from 500ms)
  • If SDK is not initialized, falls back to sendEventDirectly using persisted config from SharedPreferences (also uses EVENT retry policy)

notifyLifecycle

Send a lifecycle event. Called automatically by ProcessLifecycleOwner.

@JvmStatic
fun notifyLifecycle(event: String)
ValueMeaning
"onscreen"App moved to foreground
"background"App moved to background
"closed"Application is closing (optional)
  • Throws IllegalArgumentException if event is not one of the above values
  • Retry policy: EVENT (2 attempts, backoff from 500ms)

awaitReady

Suspend until SDK initialization completes.

suspend fun awaitReady(): Boolean

Returns true if initialization succeeded, false if it failed. This is a Kotlin-only API (suspend function without @JvmStatic). Java callers should use setListener to observe initialization instead.

lifecycleScope.launch {
    val ready = NotifyPartnersSDK.awaitReady()
    if (ready) {
        // SDK is fully initialized
    }
}

shutdown

Shut down the SDK, cancelling all pending operations. After calling this, the SDK can be re-initialized with initialize.

@JvmStatic
@Synchronized
fun shutdown()

Unregisters lifecycle observer and click interceptor.


isInitialized

@JvmStatic
fun isInitialized(): Boolean

Returns true if the SDK has been initialized.


setListener

Set a listener to receive SDK lifecycle callbacks.

@JvmStatic
fun setListener(listener: NotifyPartnersListener?)

Pass null to remove the listener. Can also be set via NotifyPartnersConfig.Builder.listener().


NotifyPartnersConfig

SDK configuration. Use Builder for a fluent setup.

data class NotifyPartnersConfig(
    val appId: String,
    val externalId: String? = null,
    val connectTimeoutMs: Long = 10_000,
    val readTimeoutMs: Long = 10_000,
    val writeTimeoutMs: Long = 10_000,
    val debugLogging: Boolean = false,
    val notificationChannelName: String = "Notifications",
    val listener: NotifyPartnersListener? = null
)
FieldTypeDefaultDescription
appIdStringApplication ID (required)
externalIdString?nullExternal user identifier
connectTimeoutMsLong10_000HTTP connect timeout (ms)
readTimeoutMsLong10_000HTTP read timeout (ms)
writeTimeoutMsLong10_000HTTP write timeout (ms)
debugLoggingBooleanfalseEnable verbose HTTP logging
notificationChannelNameString"Notifications"Notification channel display name (Android 8+)
listenerNotifyPartnersListener?nullListener for SDK callbacks

Builder:

val config = NotifyPartnersConfig.Builder("YOUR_APP_ID")
    .externalId("user-123")          // String?
    .connectTimeoutMs(15_000)         // Long
    .readTimeoutMs(15_000)            // Long
    .writeTimeoutMs(15_000)           // Long
    .debugLogging(true)               // Boolean
    .notificationChannelName("Alerts") // String
    .listener(myListener)             // NotifyPartnersListener?
    .build()

NotifyPartnersListener

Interface for SDK lifecycle callbacks.

interface NotifyPartnersListener {
    fun onInitialized(instanceId: String)
    fun onInitializationFailed(error: Throwable)
    fun onMessageReceived(msgId: String, title: String, body: String, data: Map<String, String>)
}
CallbackWhen it fires
onInitialized(instanceId)SDK registered with server successfully
onInitializationFailed(error)All retry attempts failed
onMessageReceived(msgId, title, body, data)FCM push message received

NotifyPartnersConstants

Public constants for Intent extras.

object NotifyPartnersConstants {
    const val EXTRA_MSG_ID = "notify_partners_msg_id"
    const val EXTRA_URL = "notify_partners_url"
}

ProGuard / R8

Consumer rules are included in the SDK AAR and applied automatically. They preserve the public API (NotifyPartnersSDK, NotifyPartnersConfig, NotifyPartnersListener, NotifyPartnersConstants), NotifyPartnersFirebaseService, and kotlinx.serialization data classes.