Skip to main content
Complete guide to implementing push notifications with Zixflow using Firebase Cloud Messaging (FCM).
💡 Track your campaigns: The SDK automatically tracks delivery, opens, and clicks. For advanced tracking customization, see Push Notification Tracking.

Prerequisites

Before implementing push notifications:
  1. Configure FCM in Zixflow dashboard - Add your FCM Server Key in the Zixflow dashboard first
  2. Firebase Project Setup - Create Firebase project and add your Android app
  3. Install the push messaging module - Add messagingpush dependency
Important: FCM setup is required before implementing push notifications. This integration serves as the foundation for all push messaging capabilities.

Step 1: Firebase Project Setup

1. Create Firebase Project

  1. Go to https://console.firebase.google.com
  2. Click Add project or select existing project
  3. Follow the setup wizard

2. Add Android App to Firebase

  1. In Firebase console, click Add app → Select Android
  2. Enter your app’s package name (e.g., com.yourcompany.app)
  3. (Optional) Add SHA-1 certificate for debugging
  4. Download google-services.json

3. Add google-services.json to Project

  1. Place google-services.json in your app/ directory
  2. Verify the file is at: app/google-services.json

Step 2: Add Dependencies

1. Add Google Services Plugin

Project-level build.gradle or build.gradle.kts:
// build.gradle.kts (Kotlin DSL)
buildscript {
    dependencies {
        classpath("com.google.gms:google-services:4.4.0")
    }
}
// build.gradle (Groovy DSL)
buildscript {
    dependencies {
        classpath 'com.google.gms:google-services:4.4.0'
    }
}

2. Apply Plugin in App Module

App-level build.gradle or build.gradle.kts:
// build.gradle.kts (Kotlin DSL)
plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    id("com.google.gms.google-services")  // Add this line
}
// build.gradle (Groovy DSL)
plugins {
    id 'com.android.application'
    id 'org.jetbrains.kotlin.android'
    id 'com.google.gms.google-services'  // Add this line
}

3. Add Zixflow and Firebase Dependencies

Kotlin DSL:
dependencies {
    // Zixflow core and push module
    implementation("com.zixflow.android:datapipelines:1.0.0")
    implementation("com.zixflow.android:messagingpush:1.0.0")

    // Firebase (if not already included)
    implementation(platform("com.google.firebase:firebase-bom:32.7.0"))
    implementation("com.google.firebase:firebase-messaging")
}
Groovy DSL:
dependencies {
    // Zixflow core and push module
    implementation 'com.zixflow.android:datapipelines:1.0.0'
    implementation 'com.zixflow.android:messagingpush:1.0.0'

    // Firebase
    implementation platform('com.google.firebase:firebase-bom:32.7.0')
    implementation 'com.google.firebase:firebase-messaging'
}

Step 3: Initialize Zixflow with Push Support

Basic Initialization (Kotlin)

import android.app.Application
import com.zixflow.sdk.Zixflow
import com.zixflow.sdk.ZixflowConfigBuilder
import com.zixflow.messagingpush.ModuleMessagingPushFCM

class MainApplication : Application() {

    override fun onCreate() {
        super.onCreate()

        // Initialize Zixflow SDK
        val config = ZixflowConfigBuilder(
            applicationContext = this,
            apiKey = "YOUR_API_KEY"
        )
            .autoTrackDeviceAttributes(true)
            .autoTrackActivityScreens(true)
            .trackApplicationLifecycleEvents(true)
            .logLevel(ZixflowLogLevel.DEBUG)

            // Add push notifications module
            .addModule(ModuleMessagingPushFCM())

            .build()

        Zixflow.initialize(config)
    }
}

Basic Initialization (Java)

import android.app.Application;
import com.zixflow.sdk.Zixflow;
import com.zixflow.sdk.ZixflowConfig;
import com.zixflow.sdk.ZixflowConfigBuilder;
import com.zixflow.sdk.ZixflowLogLevel;
import com.zixflow.messagingpush.ModuleMessagingPushFCM;

public class MainApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        // Initialize Zixflow SDK
        ZixflowConfig config = new ZixflowConfigBuilder(this, "YOUR_API_KEY")
            .autoTrackDeviceAttributes(true)
            .autoTrackActivityScreens(true)
            .trackApplicationLifecycleEvents(true)
            .logLevel(ZixflowLogLevel.DEBUG)
            .addModule(new ModuleMessagingPushFCM())
            .build();

        Zixflow.initialize(config);
    }
}
Important: The SDK automatically:
  • Registers a FirebaseMessagingService in your manifest
  • Fetches and registers FCM device tokens
  • Handles push notification display and tracking

Step 4: Register Application in AndroidManifest.xml

Ensure your custom Application class is registered:
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Required for push notifications -->
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

    <application
        android:name=".MainApplication"
        android:label="@string/app_name">
        <!-- Your activities -->
    </application>
</manifest>

Step 5: Request Push Notification Permission (Android 13+)

Android 13 (API 33) and higher requires runtime permission for notifications.

Request Permission in Activity

import android.Manifest
import android.os.Build
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import android.content.pm.PackageManager

class MainActivity : AppCompatActivity() {

    private val requestPermissionLauncher = registerForActivityResult(
        ActivityResultContracts.RequestPermission()
    ) { isGranted ->
        if (isGranted) {
            // Permission granted - push notifications enabled
            println("Push notification permission granted")
        } else {
            // Permission denied - explain value to user
            println("Push notification permission denied")
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Request permission for Android 13+
        requestNotificationPermission()
    }

    private fun requestNotificationPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            when {
                ContextCompat.checkSelfPermission(
                    this,
                    Manifest.permission.POST_NOTIFICATIONS
                ) == PackageManager.PERMISSION_GRANTED -> {
                    // Permission already granted
                    println("Notification permission already granted")
                }
                shouldShowRequestPermissionRationale(Manifest.permission.POST_NOTIFICATIONS) -> {
                    // Show explanation to user why you need this permission
                    showPermissionRationale()
                }
                else -> {
                    // Request permission
                    requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
                }
            }
        }
    }

    private fun showPermissionRationale() {
        // Show dialog explaining why push notifications are valuable
        // Then request permission
        requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
    }
}

Step 6: Identify Users

Critical: Device tokens must be associated with a user before push notifications can be delivered.
// Identify user when they log in
Zixflow.instance().identify(
    userId = "user@example.com",
    traits = mapOf(
        "email" to "user@example.com",
        "first_name" to "John",
        "last_name" to "Doe",
        "plan" to "premium"
    )
)
Important Notes:
  • When users start the app, they automatically generate a device token
  • This token links to their Zixflow profile when you call identify()
  • This ensures notifications are delivered to the correct user

Configuration Options

Configure push notification behavior with MessagingPushModuleConfig:
import com.zixflow.messagingpush.MessagingPushModuleConfig
import com.zixflow.messagingpush.ModuleMessagingPushFCM
import com.zixflow.messagingpush.config.PushClickBehavior

val pushConfig = MessagingPushModuleConfig.Builder()
    .setAutoTrackPushEvents(true)           // Auto-track delivered/opened
    .setPushClickBehavior(                   // Control app navigation
        PushClickBehavior.ACTIVITY_PREVENT_RESTART
    )
    .build()

val config = ZixflowConfigBuilder(this, "YOUR_API_KEY")
    .addModule(ModuleMessagingPushFCM(config = pushConfig))
    .build()

Configuration Options

OptionTypeDefaultDescription
autoTrackPushEventsBooleantrueAutomatically track delivered and opened metrics
pushClickBehaviorPushClickBehaviorACTIVITY_PREVENT_RESTARTDefine behavior when notification is clicked
notificationCallbackZixflowPushNotificationCallbacknullOverride default push behavior

Push Click Behaviors

Three strategies control app navigation when notifications are tapped: ACTIVITY_PREVENT_RESTART (Default - Recommended)
  • Reuses existing activities when possible
  • Ideal for apps with sensitive screens (checkout, payment)
  • Prevents interrupting user workflows
ACTIVITY_NO_FLAGS
  • Creates new activity instance on every notification tap
  • Standard Android behavior
RESET_TASK_STACK
  • Clears entire app state
  • Prevents back navigation to previous screens
  • Use when you want fresh app start
import com.zixflow.messagingpush.config.PushClickBehavior

val pushConfig = MessagingPushModuleConfig.Builder()
    .setPushClickBehavior(PushClickBehavior.ACTIVITY_PREVENT_RESTART)
    .build()

Advanced Features

Customize Notification Appearance

Override notification appearance using ZixflowPushNotificationCallback:
import com.zixflow.messagingpush.data.communication.ZixflowPushNotificationCallback
import com.zixflow.messagingpush.data.model.ZixflowParsedPushPayload
import androidx.core.app.NotificationCompat

val pushConfig = MessagingPushModuleConfig.Builder()
    .setNotificationCallback(object : ZixflowPushNotificationCallback {
        override fun onNotificationComposed(
            payload: ZixflowParsedPushPayload,
            builder: NotificationCompat.Builder
        ): NotificationCompat.Builder {
            // Customize notification appearance
            return builder
                .setColor(getColor(R.color.notification_accent))
                .setSound(Uri.parse("android.resource://${packageName}/${R.raw.notification_sound}"))
                .setPriority(NotificationCompat.PRIORITY_HIGH)
        }
    })
    .build()

Configure Default Icon and Color

Add metadata to AndroidManifest.xml:
<application>
    <!-- Default notification icon (should be white, transparent background) -->
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_icon"
        android:resource="@drawable/ic_notification" />

    <!-- Default notification color (accent color) -->
    <meta-data
        android:name="com.google.firebase.messaging.default_notification_color"
        android:resource="@color/notification_color" />
</application>

Rich Push Notifications

Rich push (images, videos, GIFs) is automatically supported by the SDK. No additional configuration needed. Send rich media from Zixflow dashboard:
  1. Create push notification campaign
  2. Add image URL or video URL
  3. SDK automatically downloads and displays media

Handle Existing FirebaseMessagingService

If your app already implements FirebaseMessagingService, delegate to Zixflow SDK:
import com.google.firebase.messaging.FirebaseMessagingService
import com.google.firebase.messaging.RemoteMessage
import com.zixflow.messagingpush.ZixflowFirebaseMessagingService

class MyFirebaseMessagingService : FirebaseMessagingService() {

    override fun onMessageReceived(message: RemoteMessage) {
        // Let Zixflow SDK handle push notifications
        val handled = ZixflowFirebaseMessagingService.onMessageReceived(
            context = this,
            remoteMessage = message
        )

        if (!handled) {
            // Handle non-Zixflow push notifications
            handleCustomPush(message)
        }
    }

    override fun onNewToken(token: String) {
        super.onNewToken(token)

        // Register new token with Zixflow
        ZixflowFirebaseMessagingService.onNewToken(
            context = this,
            token = token
        )

        // Handle token for other services
        handleCustomToken(token)
    }

    private fun handleCustomPush(message: RemoteMessage) {
        // Your custom push handling logic
    }

    private fun handleCustomToken(token: String) {
        // Your custom token handling logic
    }
}
Register your service in AndroidManifest.xml:
<service
    android:name=".MyFirebaseMessagingService"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

Tracking Push Metrics

The SDK automatically tracks push notification delivery, opens, and action clicks to provide campaign analytics in your Zixflow dashboard. Automatic tracking includes:
  • Delivered - When notification arrives on device
  • Opened - When user taps notification
  • Action Clicked - When user taps an action button
For advanced tracking customization, including handling custom FirebaseMessagingService implementations, action button clicks, and non-Zixflow push notifications, see the Push Notification Tracking section below.
Manual tracking (for custom implementations):
import com.zixflow.messagingpush.data.model.MetricEvent

// Track delivered event
Zixflow.instance().trackMetric(
    deliveryID = "delivery-id-from-payload",
    event = MetricEvent.delivered,
    deviceToken = "device-token"
)

// Track opened event
Zixflow.instance().trackMetric(
    deliveryID = "delivery-id-from-payload",
    event = MetricEvent.opened,
    deviceToken = "device-token"
)

Handle deep links from push notifications:

1. Add Intent Filter to Activity

<activity android:name=".MainActivity">
    <!-- Handle deep links -->
    <intent-filter android:autoVerify="true">
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />

        <data
            android:scheme="https"
            android:host="yourapp.com" />
    </intent-filter>
</activity>
class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Handle deep link
        handleDeepLink(intent)
    }

    override fun onNewIntent(intent: Intent?) {
        super.onNewIntent(intent)
        intent?.let { handleDeepLink(it) }
    }

    private fun handleDeepLink(intent: Intent) {
        val data = intent.data ?: return

        // Parse and navigate based on deep link
        when (data.path) {
            "/product" -> {
                val productId = data.getQueryParameter("id")
                navigateToProduct(productId)
            }
            "/profile" -> navigateToProfile()
            else -> {
                // Handle unknown deep link
            }
        }
    }
}

Testing Push Notifications

1. Verify Device Token Registration

Check if device token is registered:
val deviceToken = Zixflow.instance().registeredDeviceToken
if (deviceToken != null) {
    println("Device token registered: $deviceToken")
} else {
    println("Device token not yet registered")
}

2. Enable Debug Logging

Use debug logging to troubleshoot:
val config = ZixflowConfigBuilder(this, "YOUR_API_KEY")
    .logLevel(ZixflowLogLevel.DEBUG)  // Enable verbose logging
    .build()
View logs in Android Studio Logcat (filter by “Zixflow”).

3. Test on Physical Device or Emulator

Physical Device: Always works with valid google-services.json Emulator: Requires:
  • Google Play Services installed
  • Signed in with Google account
  • Internet connectivity

4. Send Test Push from Zixflow

  1. Log into Zixflow dashboard
  2. Navigate to MessagingPush Notifications
  3. Create test push notification
  4. Send to specific user (using userId from identify())

Troubleshooting

google-services.json Not Found

Error: File google-services.json is missing Solution:
  1. Verify google-services.json is in app/ directory
  2. Sync Gradle: FileSync Project with Gradle Files
  3. Clean and rebuild: BuildClean ProjectRebuild Project

Push Notifications Not Received

Symptoms: Notifications sent but not delivered Solutions:
  1. Verify FCM Server Key configured in Zixflow dashboard
  2. Ensure user is identified: Zixflow.instance().identify(userId = ...)
  3. Check POST_NOTIFICATIONS permission granted (Android 13+)
  4. Verify device token registered: Zixflow.instance().registeredDeviceToken
  5. Test on physical device or emulator with Google Play Services
  6. Check Firebase console for delivery status

Device Token Not Registering

Symptoms: registeredDeviceToken returns null Solutions:
  1. Verify google-services.json is correct and in app/ directory
  2. Ensure google-services plugin is applied
  3. Check Google Play Services available on device
  4. Verify app package name matches Firebase configuration
  5. Check logs for FCM token generation errors

Notification Not Displayed

Symptoms: Push delivered but not shown Solutions:
  1. Check notification channels (Android 8.0+)
  2. Verify notification permission granted
  3. Ensure app not in “Do Not Disturb” mode
  4. Check notification settings for your app
  5. Verify custom notification callback isn’t blocking display

Rich Media Not Loading

Symptoms: Images/videos not appearing in notifications Solutions:
  1. Verify media URL is accessible from device
  2. Check internet connectivity
  3. Ensure media format is supported (JPEG, PNG, GIF for images)
  4. Check file size (large files may timeout)

Best Practices

  1. Always identify users before sending push notifications
  2. Request permission thoughtfully - explain value before requesting
  3. Use ACTIVITY_PREVENT_RESTART for better user experience
  4. Test on multiple devices and Android versions
  5. Enable debug logging during development
  6. Monitor metrics in Zixflow dashboard
  7. Handle permission denial gracefully
  8. Test deep links thoroughly
  9. Customize notification appearance to match app branding
  10. Keep google-services.json secure - don’t commit to version control