Blog Infos
Author
Published
Topics
, , , ,
Published
Image taken From Firebase Official Website

 

In this article, we will have a look on how to send Push Notifications to Android and the recent changes from the legacy Firebase API to HTTP v1 API.

Important

June 20, 2023 — Google deprecated their Firebase Cloud Messaging (FCM) legacy HTTP API and usage of Server Key. Google recommends that you migrate your existing mobile push applications to the latest FCM HTTP v1 API on or before June 1, 2024 to avoid disruption.

Introduction to Push Notifications

Push notifications are important for mobile apps because they allow real-time communication with users. In Android development, Firebase Cloud Messaging (FCM) is commonly used for this purpose. FCM, provided by Google, makes it easy to send notifications to Android devices, offering a reliable and scalable way to deliver messages.

In order to create an account on Firebase, visit https://console.firebase.google.com/

Tap on the “Create a project” button to start the process of creating a Firebase project.

Create a project — screenshot as at June 2024

Select an account, then “Create Project”.

Once your project is created, it takes you to the dashboard, on the dashboard, tap on “Run” then “Messaging”.

Firebase Console Dashboard — screenshot as at June 2024

 

Under “Messaging” title, select the type of app to get started. Add Firebase to your Android app, set Android package name which is usually package name in AndroidManifest.xml. Download google-services.json file and add to your project.

Add Firebase to Android App — screenshot as at June 2024

 

Then follow the instructions to add Firebase to your app.

Download google-services.json — screenshot as at June 2024

 

Add Firebase services to your app
Using Firebase BoM (Bill of Materials)

When you work with Firebase in an Android project using Gradle, using the Firebase BoM can simplify how you handle dependencies. Here’s how it helps:

  1. Version Management: With Firebase BoM, you only need to specify the version once. All Firebase libraries will automatically use this version, so you don’t have to set the version for each library individually.
  2. Version Consistency: This approach ensures all Firebase libraries are compatible with each other, reducing the risk of errors caused by version mismatches.
dependencies {
  // Import the Firebase BoM, check for the latest version
  implementation(platform("com.google.firebase:firebase-bom:33.1.1"))
}
Adding Firebase Messaging Individually
  1. Manual Version Management: You need to set the version for each Firebase dependency by yourself. This means you have to update and manage each version, which can be tricky and might lead to inconsistencies.
  2. Potential for Conflicts: Without using the BoM, there’s a greater chance of having version conflicts between different Firebase libraries. This can cause issues like deprecated methods or compatibility problems.
dependencies {
    // Check for the latest version
    implementation 'com.google.firebase:firebase-messaging:23.2.0'
}
Follow the implementation details in the setup instructions:
class FirebaseNotificationService : FirebaseMessagingService() {

    override fun onNewToken(token: String) {
        super.onNewToken(token)
        Log.d(TAG, "FCM: Refreshed token: $token")
        
        // Implement method to send new token to your app's server.
        sendRefreshedTokenToServer(token);
    }

    override fun onMessageReceived(remoteMessage: RemoteMessage) {
        super.onMessageReceived(remoteMessage)
        // Handle FCM messages here.
        Log.d(TAG, "FCM: From: ${remoteMessage.from}")

        // Check if message contains a data payload using getData()
        remoteMessage.getData().isNotEmpty().let {
            Log.d(TAG, "FCM: Message data payload: " + remoteMessage.getData())
            sendNotification(
              title = remoteMessage.getData().title,
              body = remoteMessage.getData().body
            )
        }

        // Check if message contains a notification payload.
        remoteMessage.notification?.let {
            Log.d(TAG, "FCM: Message Notification Body: ${it.body}")
        }
    }
}
Send Notification:
private fun sendNotification(title: String, body: String) {
        val intent = Intent(this, MainActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
        val pendingIntent = PendingIntent.getActivity(
            this,
            0,
            intent,
            PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
        )

        val notificationChannelStr = "CHANNEL"
        val defaultSoundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)
        val notificationBuilder = NotificationCompat.Builder(this, notificationChannelStr)
            .setSmallIcon(R.drawable.ic_notification_icon)
            .setContentTitle(title)
            .setContentText(body)
            .setAutoCancel(true)
            .setSound(defaultSoundUri)
            .setContentIntent(pendingIntent)

        val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager

        // Since Android Oreo, notification channels are needed.
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val channel = NotificationChannel(
                notificationChannelStr,
                resources.getString(R.string.notification_channel_title),
                NotificationManager.IMPORTANCE_DEFAULT)
            notificationManager.createNotificationChannel(channel)
        }

        // Unique ID for each notification to prevent notifications replacing each other
        val notificationId = System.currentTimeMillis().toInt()
        notificationManager.notify(notificationId, notificationBuilder.build())
    }
AndroidManifest file:
<service
    android:name=".FirebaseNotificationService"
    android:exported="true">
    <intent-filter>
      <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

To test to see if the setup works, go to your Firebase console.

Test Notification Messages
To Compose a New Notification

Navigate to “Messaging”, under “Campaign” tab, click on the “New Campaign” button.

  • Title: Enter the title of your notification. This will be displayed in bold on the recipient’s device.
  • Text: Enter the main content or body of your notification. This is the message that will be displayed below the title.

Test Data Messages

To send test data messages, you can use the “Additional Options” on the “Compose notification” screen.

Additional Options
  • Image: You can add a URL to an image to display in the notification.
  • Custom Data: Add key-value pairs to include additional data with your notification.

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

No results found.

Jobs

Target the Notification
  • Target: Choose your target audience. You can target by app, specific users (using user properties or user segments), or topics.
  • App: If you choose to target your app, you can select specific devices or all devices that have your app installed.
  • User Segment: You can select predefined user segments.
  • Topic: You can target users subscribed to a specific topic.
Receiving notifications even when app is on background or killed
Data messages

This enables sending messages as Key-value pairs which goes directly to the onMessageReceived() method regardless of whether app is on foregroundbackground or killed.

{
  "message": {
    "token": "token",
    "android": {
      "priority": "high"
    },
    "data": {
      "title": "FCM title",
      "body": "This is an FCM data message!"
    }
  }
}
The notification messages

This section must be omitted to ensure your data messages are delivered to onMessageReceived() method even when app is on background or killed.

{
   "message":{
      "token":"token",
      "android": {
        "priority": "high"
      },
      "notification":{
        "title":"FCM Message",
        "body":"This is an FCM notification message!"
      }
   }
}
Priority

When sending messages to Android devices, you can set the delivery priority to either normal or high.

Normal Priority
  • Default setting for data messages and delivers immediately IF the device is awake, it is delayed if the device is in Doze Mode to save battery.
High Priority
  • Delivered immediately, even if the device is asleep.
  • Can wake the device and run limited processing.
  • Use for urgent messages that require user interaction.
From Firebase docs:

When your app is in the background, Android directs notification messages to the system tray. A user tap on the notification opens the app launcher by default.

This includes messages that contain both notification and data payload (and all messages sent from the Notifications console).

In these cases, the notification is delivered to the device’s system tray, and the data payload is delivered in the extras of the intent of your launcher Activity.

Migrate from Legacy Firebase API to HTTP v1 API

The new FCM v1 API uses OAuth2 for security. If an access token is leaked, it only works for about an hour before it expires. Refresh tokens are sent less often, making them less likely to be stolen.

Authorisation for Send Requests:

Before
  • Used a Server Key string obtained from Firebase console.
  • Header: Authorization: key=AIzaSyZ-1u...0GBYzPu7Udno5aA
Now
  • Requires an OAuth 2.0 access token.
  • Header: Authorization: Bearer <valid OAuth 2.0 token>
Cross Platform Support

The legacy API allows sending messages to multiple platforms, but it gets complicated as you add more features. The new FCM API makes this process much easier using common top-level fields.

Versioned path & send request

The URL now includes /v1, the URL includes your Firebase project’s ID in the format /projects/myproject-ID/

// From Legacy endpoint
POST https://fcm.googleapis.com/fcm/send

// To HTTP v1 API
POST https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send
// From Legacy - authorization key
Authorization: key=AIzaSyZ-1u...0GBYzPu7Udno5aA

// To HTTP v1 API - Addition of use of "Bearer"
Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA
Payload of send requests
// From Legacy payload
{
  "to": "/topics/news",
  "notification": {
    "title": "Breaking News",
    "body": "New news story available."
  },
  "data": {
    "story_id": "story_12345"
  }
}

// To HTTP v1 API payload
{
  "message": {
    "topic": "news",
    "notification": {
      "title": "Breaking News",
      "body": "New news story available."
    },
    "data": {
      "story_id": "story_12345"
    }
  }
}
Nested JSON Data

The HTTP v1 API does not support nested JSON values in the data field like the old messaging API. You need to convert nested JSON to a string.

// From Legacy payload with JSON Data
{
  ...
  "data": {
    "keysandvalues": {"key1": "value1", "key2": 123}
  }
}

// To HTTP v1 API payload with JSON Data
{
  "message": {
   ...
    "data": {
      "keysandvalues": "{\"key1\": \"value1\", \"key2\": 123}"
    }
  }
}
Targeting Specific Devices using Device token

To target specific devices with the HTTP v1 API, use the device’s current registration token in the token key instead of the to key.

// From Legacy payload to target using device token
{
  "notification": {
   "body": "This is an FCM notification message!",
      "title": "FCM Message"
    },
  "to" : "bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}

// To HTTP v1 API payload to target using device token
{
   "message":{
      "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1...",
      "notification":{
        "body":"This is an FCM notification message!",
        "title":"FCM Message"
      }
   }
}
Example with the cURL command:
curl -X POST -H "Authorization: Bearer ya29.ElqKBGN2Ri_Uz...HnS_uNreA" -H "Content-Type: application/json" -d '{
"message":{
   "notification":{
     "title":"FCM Message",
     "body":"This is an FCM Message"
   },
   "token":"bk3RNwTe3H0:CI2k_HHwgIpoDKCIZvvDMExUdFQ3P1..."
}}' https://fcm.googleapis.com/v1/projects/myproject-b5ae1/messages:send
Sending Data Messages using cURL from the Terminal

cURL is a command line tool and library for transferring data with URL syntax. To send messages, you need to setup Google Cloud on your machine.

// Then, install the Google Cloud SDK via Homebrew:
brew install --cask google-cloud-sdk

// After installation, initialise the SDK to set up your environment:
gcloud init

This prompts or launches your Google account on the Web to login and set up the SDK with your project and configuration preferences.

Add SDK to Your Path:

The installer prompts to update your .bash_profile.zshrc, or .profile file with the Google Cloud SDK path. If not, you can add it manually by editing your profile file and adding:

export PATH=$PATH:/path/to/google-cloud-sdk/bin
#!/bin/bash

# Replace with your Firebase project ID
PROJECT_ID="project-name"

# Obtain an OAuth 2.0 token
ACCESS_TOKEN=$(gcloud auth application-default print-access-token)

# Replace with your FCM device token
FCM_TOKEN="fcm-token"

# Data payload and other key values
NOTIFICATION_PAYLOAD='{
  "message": {
    "token": "'"$FCM_TOKEN"'",
    "android": {
      "priority": "high"
    },
    "data": {
      "title":"FCM Message",
      "body":"This is an FCM notification message!",
      "key_one": "value_one",
      "key_two": "value_two",
      "key_three": "value_three"
    }
  }
}'

# Send the notification using curl
curl -X POST \
  -H "Authorization: Bearer $ACCESS_TOKEN" \
  -H "Content-Type: application/json; UTF-8" \
  -d "$NOTIFICATION_PAYLOAD" \
  "https://fcm.googleapis.com/v1/projects/$PROJECT_ID/messages:send"
Sources:

This article is previously published on proandroiddev.com

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
Nowadays authentication has become common in almost all apps. And many of us know…
READ MORE
blog
Yes! You heard it right. We’ll try to understand the complete OTP (one time…
READ MORE
blog
Firebase Remote Config is a cloud service that lets you change the behavior and…
READ MORE
blog
Using annotations in Kotlin has some nuances that are useful to know
READ MORE
Menu