Firebase Cloud Messaging (FCM)

Follow this procedure to implement push notification with FCM.

Introduction to FCM push notification

The push notification solution described in this section is based on the Firebase Cloud Messaging service. Familiarize yourself with FCM by visiting the Firebase Cloud Messaging web site.
Important:

Firebase Cloud Messaging comes with implicit features, that you need to consider carefuly. For example, user data collection for analytics can be done by FCM, when doing push notifications; This feature is disabled by default, when building Genero apps for Android, and can be activated with the -bfac gmabuildtool option. Take the time to read the documentation that is available at Firebase Cloud Messaging.

Firebase Cloud Messaging services allow push servers to send notification message data to registered Androidâ„¢ or iOS devices.

The system involves the following actors:
  • The Firebase Cloud Messaging service (FCM):

    FCM provides push server and client identification. It also handles all aspects of queuing of messages and delivery to the target application running on registered devices.

  • The registration tokens maintainer:

    A Web services server program maintaining the database of registration tokens with application user information. This program must listen to new device registration events and store them in a database. The push server program can then query this database to build the list of registration tokens to identify the devices to be notified.

  • The push server program:

    Implemented by a third-party service or as a Genero BDL program using the Web services API. This push server program will send notification messages to FCM with two connection servers (HTTP and XMPP).

  • Devices running the Genero app registered to the push notification server:

    Registered devices use the push notification client API to register, get notifications data and unregister from the service.

Note:

The database used to store registration tokens must be a multi-user database (do not use SQLite for example), since two distinct programs will use the database.

Creating a FCM project

To initiate a push notification service dedicated to your applications, you must first create a Firebase Cloud Messaging project in the FCM console. Creating an FCM project will provide you:
  • The google-services.json configuration file, to be bundled with your app.
  • The "Server Key" is the authentication key to access Google services.
Steps to setup your FCM projet:
  1. Go to the FCM console and login with your Google developer account.
  2. Add a new FCM project.
  3. Add an (Android) app to your FCM project: Specify the same package name specified when building your app with the --build-app-package-name option of gmabuildtool.
  4. Download the google-services.json configuration file. This file needs to be added to the appdir, beside other program files.
  5. Skip other FCM project creation steps.
  6. In the project overview page, go to the settings of the new created project.
  7. Select the "Cloud Messaging" panel.
  8. Copy the "Server Key" and save it to a file. This is the key to be send with the "Authorization:key=server-key" HTTP header when posting a message to the FCM server endpoint fcm.googleapis.com/fcm/send.

For more details about FCM project creation, visit the Firebase Cloud Messaging web site.

Implementing the registration tokens maintainer

To handle device registrations on the server side of your application, the same code base can be used for FCM and other token-based frameworks.

For more details, see Implementing a token maintainer.

Implementing the push server

The push server will produce application notification messages that will be transmitted to the FCM service. The FCM service will then spread them to all mobile devices registered to the service with the Sender ID.

Important:

The size of an FCM notification content cannot exceed 4 Kilobytes. If more information needs to be passed, after receiving the push message, apps must contact the server part to query for more information. However, this is only possible when network is available.

The push server will use RESTFul HTTP POST requests to send notifications through the FCM service to the following URL:

"https://fcm.googleapis.com/fcm/send".

The HTTP POST header must contain the following attributes:
Content-Type:application/json
Authorization:key=server-key

where server-key is the Server Key obtained from the FCM project settings.

The push server program can be implemented with the Web Services API to make RESTFul requests as follows:
IMPORT com
IMPORT util

FUNCTION fcm_send_notif_http(server_key, notif_obj)
    DEFINE server_key STRING,
           notif_obj util.JSONObject
    DEFINE req com.HttpRequest,
           resp com.HttpResponse,
           req_msg, res STRING
    TRY
        LET req = com.HttpRequest.Create("https://fcm.googleapis.com/fcm/send")
        CALL req.setHeader("Content-Type", "application/json")
        CALL req.setHeader("Authorization", SFMT("key=%1", server_key))
        CALL req.setMethod("POST")
        LET req_msg = notif_obj.toString()
        IF req_msg.getLength() > 4096 THEN
           LET res = "ERROR : GCM message cannot exceed 4 kilobytes"
           RETURN res
        END IF
        CALL req.doTextRequest(req_msg)
        LET resp = req.getResponse()
        IF resp.getStatusCode() != 200 THEN
            LET res = SFMT("HTTP Error (%1) %2",
                           resp.getStatusCode(),
                           resp.getStatusDescription())
        ELSE
            LET res = "Push notification sent!"
        END IF
    CATCH
        LET res = SFMT("ERROR : %1 (%2)", status, sqlca.sqlerrm)
    END TRY
    RETURN res
END FUNCTION
The body of the HTTP POST request must be a JSON formatted record using a structure similar to the following example:
{
  "collapse_key":  "stock_update",
  "time_to_live": 108,
  "delay_while_idle": true,
  "data":
  {
    "stock_change":
    {
      "stock_id" : "STK-034" ,
      "timestamp" : "2015-02-24 15:10:34.18345",
      "item_count" : 15023
    },
  },
  "registration_ids" : [ "APA91b...", "Hun4MxP...", "5ego..." ]
}
Note:

This notification message uses the "registration_ids" attribute to provide a list of devices to be notified. If you want to notify a single device, use the "to" attribute instead of "registration_ids", and pass a single registration token instead of a JSON array.

For more details about the JSON request structure in a FCM HTTP POST, visit the Firebase Cloud Messaging web site.

By convention, if the "data" member of the JSON request defines a "genero_notification" member, the front-end will show graphical notification (pop-up hint) with the "title", "content" and the "icon" values.
Note:

The recommendation for GMA is that the icon is packaged in the APK and is accessible by name (as the gma_ic_genero.png in the drawable folders).

For example:
...
  "data":
  {
    "genero_notification":
    {
      "title":    "Stock has changed",
      "content":  "New stock information will be retrieved from the backend server...",
      "icon":     "stock_update"
    },
    ...
  },
  "registration_ids" : [ "APA91b...", "Hun4MxP...", "5ego..." ]
}
The next code example implements a function that creates the JSON object, which can be passed to the fcm_send_notif_http() function described above. The only purpose of this notification message is to test the "genero_notification" pop-up hint. The function takes an array of registration tokens as a parameter, which will be used to set the "registration_ids" attribute:
FUNCTION fcm_simple_popup_notif(reg_ids, notif_obj, popup_msg, user_data)
    DEFINE reg_ids DYNAMIC ARRAY OF STRING,
           notif_obj util.JSONObject,
           popup_msg, user_data STRING
    DEFINE data_obj, popup_obj util.JSONObject

    CALL notif_obj.put("registration_ids", reg_ids)

    LET data_obj = util.JSONObject.create()

    LET popup_obj = util.JSONObject.create()
    CALL popup_obj.put("title", "Push demo")
    CALL popup_obj.put("content", popup_msg)
    CALL popup_obj.put("icon", "genero")

    CALL data_obj.put("genero_notification", popup_obj)
    CALL data_obj.put("other_info", user_data)

    CALL notif_obj.put("data", data_obj)

END FUNCTION
The fcm_simple_popup_notif() and fcm_send_notif_http() functions can then be used as follows:
IMPORT com
IMPORT util

MAIN
    CONSTANT server_key = "xyz..."
    DEFINE reg_ids DYNAMIC ARRAY OF STRING,
           notif_obj util.JSONObject

    LET reg_ids[1] = "APA91bHun..."
    LET reg_ids[2] = "B4AA2q7xa..."

    LET notif_obj = util.JSONObject.create()
    CALL fcm_simple_popup_notif(reg_ids, notif_obj, "This is my message!")
    CALL fcm_send_notif_http(server_key, notif_obj)

END MAIN
In order to use the tokens database maintained by a token maintainer program, your FCM push server can collect registration tokens as shown in the following example:
FUNCTION fcm_collect_tokens(reg_ids)
    DEFINE reg_ids DYNAMIC ARRAY OF STRING
    DEFINE rec RECORD
               id INTEGER,
               notification_type VARCHAR(10),
               registration_token VARCHAR(250),
               badge_number INTEGER,
               app_user VARCHAR(50),
               reg_date DATETIME YEAR TO FRACTION(3)
           END RECORD
    DECLARE c1 CURSOR FOR
      SELECT * FROM tokens
       WHERE notification_type = "FCM"
    CALL reg_ids.clear()
    FOREACH c1 INTO rec.*
        CALL reg_ids.appendElement()
        LET reg_ids[reg_ids.getLength()] = rec.registration_token
    END FOREACH
END FUNCTION
The above function can then be used by another function to send the push message to all registered devices:
FUNCTION fcm_send_text(server_key, msg_title, user_data)
    DEFINE server_key, msg_title, user_data STRING
    DEFINE reg_ids DYNAMIC ARRAY OF STRING,
           notif_obj util.JSONObject,
           info_msg STRING
    CALL fcm_collect_tokens(reg_ids)
    IF reg_ids.getLength() == 0 THEN
       RETURN "No registered devices..."
    END IF
    LET notif_obj = util.JSONObject.create()
    CALL fcm_simple_popup_notif(reg_ids, notif_obj, msg_title, user_data)
    LET info_msg = fcm_send_notif_http(server_key, notif_obj)
    RETURN info_msg
END FUNCTION

Handle push notifications in mobile apps

To handle push notifications in mobile apps, the same code base can be used for FCM and other token-based frameworks.

For more details see Handling notifications in the mobile app.