Titanium Firebase Cloud Messaging Save

Use the Firebase Cloud Messaging SDK in Axway Titanium πŸš€ Edit

Project README

Firebase Cloud Messaging - Titanium Module

Use the native Firebase SDK (iOS/Android) in Axway Titanium. This repository is part of the Titanium Firebase project.

Supporting this effort

The whole Firebase support in Titanium is developed and maintained by the community (@hansemannn and @m1ga). To keep this project maintained and be able to use the latest Firebase SDK's, please see the "Sponsor" button of this repository, thank you!

Topics

Requirements

Download

iOS notes:

To register for push notifications on iOS, you only need to call the Titanium related methods as the following:

// Listen to the notification settings event
Ti.App.iOS.addEventListener('usernotificationsettings', function eventUserNotificationSettings() {
  // Remove the event again to prevent duplicate calls through the Firebase API
  Ti.App.iOS.removeEventListener('usernotificationsettings', eventUserNotificationSettings);

  // Register for push notifications
  Ti.Network.registerForPushNotifications({
    success: function () { ... },
    error: function () { ... },
    callback: function () { ... } // Fired for all kind of notifications (foreground, background & closed)
  });
});

// Register for the notification settings event
Ti.App.iOS.registerUserNotificationSettings({
  types: [
    Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
    Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
    Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
  ]
});

Android Notes:

Big image notification with colored icon/appname Big text notification with colored icon/appname

Register for push

If you use Titanium 12.0.0+ you can use

Ti.Network.registerForPushNotifications({
  success: function () { ... },
  error: function () { ... }
});

to request Android 13 runtime permissions. All other version < Android 13 will call the success function right away.

If you have runtime permissions (the success event mentioned above or Ti.Network.remoteNotificationsEnabled is true) you can call fcm.registerForPushNotifications() to request a token. Check the full example below for all steps.

Android 13 permission

If you compile your app with Titanium <=11.1.0.GA (target SDK 31) and Android 13 phone will ask for Push runtime permission the first time you create a notification channel. If you use Titanium >=12.0.0 (target SDK 33) you can use Ti.Network.registerForPushNotifications() to ask for the permission. You can also request the permission with older SDKs yourself by using the general requestPermissions() method:

var permissions = ['android.permission.POST_NOTIFICATIONS'];
Ti.Android.requestPermissions(permissions, function(e) {
  if (e.success) {
    Ti.API.info('SUCCESS');
  } else {
    Ti.API.info('ERROR: ' + e.error);
});

Setting the Notification Icon

For a data notification you have to place a notification icon "notificationicon.png" into the following folder: [application_name]/[app*]/platform/android/res/drawable/ or [application_name]/[app*]/platform/android/res/drawable-* (if you use custom dpi folders)

* = Alloy

To use the custom icon for a notification message you need to add this attribute within the <application/> section of your tiapp.xml:

<meta-data android:name="com.google.firebase.messaging.default_notification_icon" android:resource="@drawable/notificationicon"/>

Otherwise the default icon will be used.

It should be flat (no gradients), white and face-on perspective and have a transparent background. The icon will only show the outline/shape of your icon so make sure all you e.g. white is transparent otherwise it will just be a square.

Note: You should generate the icon for all resolutions.

22 Γ— 22 area in 24 Γ— 24 (mdpi)
33 Γ— 33 area in 36 Γ— 36 (hdpi)
44 Γ— 44 area in 48 Γ— 48 (xhdpi)
66 Γ— 66 area in 72 Γ— 72 (xxhdpi)
88 Γ— 88 area in 96 Γ— 96 (xxxhdpi)

You can use this script to generate it once you put the icon in drawable-xxxhdpi/notificationicon.png and have Image Magick installed. On macOS, you can install it using brew install imagemagick, on Windows you can download it here.

#!/bin/sh

ICON_SOURCE="app/platform/android/res/drawable-xxxhdpi/notificationicon.png"
if [ -f "$ICON_SOURCE" ]; then
    mkdir -p "app/platform/android/res/drawable-xxhdpi"
    mkdir -p "app/platform/android/res/drawable-xhdpi"
    mkdir -p "app/platform/android/res/drawable-hdpi"
    mkdir -p "app/platform/android/res/drawable-mdpi"
    convert "$ICON_SOURCE" -resize 72x72 "app/platform/android/res/drawable-xxhdpi/notificationicon.png"
    convert "$ICON_SOURCE" -resize 48x48 "app/platform/android/res/drawable-xhdpi/notificationicon.png"
    convert "$ICON_SOURCE" -resize 36x36 "app/platform/android/res/drawable-hdpi/notificationicon.png"
    convert "$ICON_SOURCE" -resize 24x24 "app/platform/android/res/drawable-mdpi/notificationicon.png"
else
    echo "No 'notificationicon.png' file found in app/platform/android/res/drawable-xxxhdpi"
fi

Data / Notification messages

On Android there are two different messages that the phone can process: Notification messages and Data messages. A Notification message is processed by the system, the Data message is handeled by showNotification() in TiFirebaseMessagingService. Using the notification block inside the POSTFIELDS will send a Notification message.

Supported data fields:

  • "title" => "string"
  • "message" => "string"
  • "big_text" => "string"
  • "big_text_summary" => "string"
  • "icon" => "Remote URL"
  • "image" => "Remote URL"
  • "rounded_large_icon" => "Boolean" (to display the largeIcon as a rounded image when the icon field is present)
  • "force_show_in_foreground" => "Boolean" (show notification even app is in foreground)
  • "id" => "int"
  • "color" => will tint the app name and the small icon next to it
  • "vibrate" => "boolean"
  • "sound" => "string" (e.g. "notification.mp3" will play /platform/android/res/raw/notification.mp3)
  • "badge" => "int" (if supported by the phone it will show a badge with this number)

Supported notification fields:

  • "title" => "string"
  • "body" => "string"
  • "color" => "#00ff00",
  • "tag" => "custom_notification_tag", // push with the same tag will replace each other
  • "sound" => "string" (e.g. "notification.mp3" will play /platform/android/res/raw/notification.mp3)

Android: Note about custom sounds

To use a custom sound you have to create a second channel. The default channel will always use the default notification sound on the device! If you send a normal or mixed notification you have to set the android_channel_id in the notification node. If you send a data notification the key is called channelId. Chech extended PHP Android example for a PHP example.

Android: Note for switching between v<=v2.0.2 and >=v2.0.3 if you use notification channels with custom sounds

With versions prior to 2.0.3 of this module, FirebaseCloudMessaging.createNotificationChannel would create the notification sound uri using the resource id of the sound file in the res/raw directory. However, as described in this android issue, those resource ids can change to reference different files (or no file) between app versions, and that happens the notification channel may play a different or no sound than originally intended. With version 2.0.3 and later, we now create the uri's using the string filename so that it will not change if resource ids change. So if you are on version <=2.0.2 and are switching to version >=2.0.3, you will want to check if this is a problem for you by installing a test app using version >= 2.0.3 as an upgrade to a previous test app using version <= 2.0.2. Note that you should not uninstall the first app before installing the second app; nor should you reset user data. If it is a problem you can workaround by first deleting the existing channel using deleteNotificationChannel, and then recreating the channel with the same settings as before, except with a different id. Don't forget that your push server will need to be version aware and send to this new channel for newer versions of your apps.

Errors with firebase.analytics

If you run into errors in combination with firebase.analytics e.g. Error: Attempt to invoke virtual method 'getInstanceId()' on a null object reference you can add:

<service android:name="com.google.firebase.components.ComponentDiscoveryService" >
	<meta-data android:name="com.google.firebase.components:com.google.firebase.iid.Registrar"
		android:value="com.google.firebase.components.ComponentRegistrar" />
</service>

to the tiapp.xml

API

FirebaseCloudMessaging

Methods

registerForPushNotifications()

appDidReceiveMessage(parameters) (iOS only)

  • parameters (Object)

Note: Only call this method if method swizzling is disabled (enabled by default). Messages are received via the native delegates instead, so receive the gcm.message_id key from the notification payload instead.

sendMessage(parameters)

  • parameters (Object)
    • messageID (String)
    • to (String)
    • timeToLive (Number)
    • data (Object)

subscribeToTopic(topic)

  • topic (String)

unsubscribeFromTopic(topic)

  • topic (String)

setNotificationChannel(channel) - Android-only

  • channel (NotificationChannel Object) Use Ti.Android.NotificationManager.createNotificationChannel() to create the channel and pass it to the function. See Titanium.Android.NotificationChannel

Prefered way to set a channel. As an alternative you can use createNotificationChannel()

createNotificationChannel(parameters) - Android-only

  • parameters (Object)

    • sound (String) optional, refers to a sound file (without extension) at platform/android/res/raw. If sound == "default" or not passed in, will use the default sound. If sound == "silent" the channel will have no sound
    • channelId (String) optional, defaults to "default"
    • channelName (String) optional, defaults to channelId
    • importance (String) optional, either "low", "high", "default". Defaults to "default", unless sound == "silent", then defaults to "low".
    • lights (Boolean) optional, defaults to false
    • showBadge (Boolean) optional, defaults to false

    Read more in the official Android docs.

deleteNotificationChannel(channelId) - Android-only

  • channelId (String) - same as the id used to create in createNotificationChannel

setForceShowInForeground(showInForeground) - Android-only

  • showInForeground (Boolean) Force the notifications to be shown in foreground.

clearLastData() - Android-only

  • Will empty the stored lastData values.

getToken() - Android-only

  • Returns the current FCM token.

deleteToken() - Android-only

  • Removes the current FCM token.

Properties

shouldEstablishDirectChannel (Number, get/set)

fcmToken (String, get)

apnsToken (String, set) (iOS only)

lastData (Object) (Android only) The propery lastData will contain the data part when you send a notification push message (so both nodes are visible inside the push payload). Read before calling registerForPushNotifications().

Events

didReceiveMessage

  • message (Object)

    iOS Note: This method is only called on iOS 10+ and only for direct messages sent by Firebase. Normal Firebase push notifications are still delivered via the Titanium notification events, e.g.

    Ti.App.iOS.addEventListener('notification', function(event) {
      // Handle foreground notification
    });
    
    Ti.App.iOS.addEventListener('remotenotificationaction', function(event) {
      // Handle background notification action click
    });
    

didRefreshRegistrationToken

  • fcmToken (String)

success (Android only)

  • will fire on Android 13 after you call registerForPushNotifications to allow Push notifications

error (Android only)

  • error (String): Error during token registration or user denied registerForPushNotifications

subscribe (Android only)

  • success (Boolean): Successfully subscribed

unsubscribe (Android only)

  • success (Boolean): Successfully unsubscribed

tokenRemoved (Android only)

  • success (Boolean): Successfully removed token

Example

Titanium 12.0.0.GA+

if (OS_IOS) {
	const FirebaseCore = require('firebase.core');
	FirebaseCore.configure();
}

// Important: The cloud messaging module has to imported after (!) the configure()
// method of the core module is called
const FirebaseCloudMessaging = require('firebase.cloudmessaging');

// Called when the Firebase token is registered or refreshed.
FirebaseCloudMessaging.addEventListener('didRefreshRegistrationToken', onToken);

// Called when direct messages arrive. Note that these are different from push notifications.
FirebaseCloudMessaging.addEventListener('didReceiveMessage', function(e) {
	Ti.API.info('Message', e.message);
});


if (OS_ANDROID) {
	// Android

	// create a notification channel
	const channel = Ti.Android.NotificationManager.createNotificationChannel({
		id: 'default', // if you use a custom id you have to set the same to the `channelId` in you php send script!
		name: 'Default channel',
		importance: Ti.Android.IMPORTANCE_DEFAULT,
		enableLights: true,
		enableVibration: true,
		showBadge: true
	});
	FirebaseCloudMessaging.notificationChannel = channel;

	// display last push data if available
	Ti.API.info(`Last data: ${FirebaseCloudMessaging.lastData}`);

	// request push permission
	requestPushPermissions();
} else {
	// iOS
	// Listen to the notification settings event
	Ti.App.iOS.addEventListener('usernotificationsettings', function eventUserNotificationSettings() {
		// Remove the event again to prevent duplicate calls through the Firebase API
		Ti.App.iOS.removeEventListener('usernotificationsettings', eventUserNotificationSettings);
		requestPushPermissions();
	});

	// Register for the notification settings event
	Ti.App.iOS.registerUserNotificationSettings({
		types: [
			Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
			Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
			Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
		]
	});
}

function requestPushPermissions() {
	// Register for push notifications
	Ti.Network.registerForPushNotifications({
		success: function(e) {
			// Register the device with the FCM service.
			if (OS_ANDROID) {
				// register for a token
				FirebaseCloudMessaging.registerForPushNotifications();
			} else {
				// iOS
				onToken(e);
			}
		},
		error: function(e) {
			Ti.API.error(e);
		},
		callback: function(e) {
			// Fired for all kind of notifications (foreground, background & closed)
			Ti.API.info(e.data);
		}
	});
}

function onToken(e) {
	// new device is registered

	if (OS_ANDROID) {
		Ti.API.info("New token", e.fcmToken);
	} else {
		if (FirebaseCloudMessaging != null) {
			Ti.API.info("New token", FirebaseCloudMessaging.fcmToken);
		}
	}
}

// Check if token is already available.
if (FirebaseCloudMessaging.fcmToken) {
	Ti.API.info('FCM-Token', FirebaseCloudMessaging.fcmToken);
} else {
	Ti.API.info('Token is empty. Waiting for the token callback ...');
}

// Subscribe to a topic.
FirebaseCloudMessaging.subscribeToTopic('testTopic');

Titanium <=11.1.0.GA

if (OS_IOS) {
  const FirebaseCore = require('firebase.core');
  FirebaseCore.configure();
}

// Important: The cloud messaging module has to imported after (!) the configure()
// method of the core module is called
const FirebaseCloudMessaging = require('firebase.cloudmessaging');

// Called when the Firebase token is registered or refreshed.
FirebaseCloudMessaging.addEventListener('didRefreshRegistrationToken', function(e) {
    Ti.API.info('Token', e.fcmToken);
});

// Called when direct messages arrive. Note that these are different from push notifications.
FirebaseCloudMessaging.addEventListener('didReceiveMessage', function(e) {
    Ti.API.info('Message', e.message);
});

// Android-only: For configuring custom sounds and importance for the generated system
// notifications when app is in the background
if (OS_ANDROID) {
    const channel = Ti.Android.NotificationManager.createNotificationChannel({
        id: 'default',   // if you use a custom id you have to set the same to the `channelId` in you php send script!
        name: 'Default channel',
        importance: Ti.Android.IMPORTANCE_DEFAULT,
        enableLights: true,
        enableVibration: true,
        showBadge: true
    });


    FirebaseCloudMessaging.notificationChannel = channel;

    // display last data:
    Ti.API.info(`Last data: ${FirebaseCloudMessaging.lastData}`);
} else {
	// iOS
	// Listen to the notification settings event
	Ti.App.iOS.addEventListener('usernotificationsettings', function eventUserNotificationSettings() {
	  // Remove the event again to prevent duplicate calls through the Firebase API
	  Ti.App.iOS.removeEventListener('usernotificationsettings', eventUserNotificationSettings);

	  // Register for push notifications
	  Ti.Network.registerForPushNotifications({
	    success: function () {
            if (!!FirebaseCloudMessaging) {
                console.log('New token', FirebaseCloudMessaging.fcmToken);
            }
        },
	    error: function (e) {
            console.error(e);
        },
	    callback: function (e) {
            // Fired for all kind of notifications (foreground, background & closed)
            console.log(e.data);
        }
	  });
	});

	// Register for the notification settings event
	Ti.App.iOS.registerUserNotificationSettings({
	  types: [
	    Ti.App.iOS.USER_NOTIFICATION_TYPE_ALERT,
	    Ti.App.iOS.USER_NOTIFICATION_TYPE_SOUND,
	    Ti.App.iOS.USER_NOTIFICATION_TYPE_BADGE
	  ]
	});
}

// Register the device with the FCM service.
if (OS_ANDROID) {
    FirebaseCloudMessaging.registerForPushNotifications();
}

// Check if token is already available.
if (FirebaseCloudMessaging.fcmToken) {
    Ti.API.info('FCM-Token', FirebaseCloudMessaging.fcmToken);
} else {
    Ti.API.info('Token is empty. Waiting for the token callback ...');
}

// Subscribe to a topic.
FirebaseCloudMessaging.subscribeToTopic('testTopic');

Example to get the the resume data/notification click data on Android:

const handleNotificationData = (notifObj) => {
	if (notifObj) {
		notifData = JSON.parse(notifObj);
		// ...process notification data...
		FirebaseCloudMessaging.clearLastData();
	}
}

// Check if app was launched on notification click
const launchIntent = Ti.Android.rootActivity.intent;
handleNotificationData(launchIntent.getStringExtra("fcm_data"));

Ti.App.addEventListener('resumed', function() {
	// App was resumed from background on notification click
	const currIntent = Titanium.Android.currentActivity.intent;
	const notifData = currIntent.getStringExtra("fcm_data");
	handleNotificationData(notifData);
});

Sending push messages

using curl

Data message:

curl -i -H 'Content-type: application/json' -H 'Authorization: key=#####KEY#####' -XPOST https://fcm.googleapis.com/fcm/send -d '{
  "registration_ids":["####DEVICE_ID#####"],
  "data": {"title":"Push Title", "message":"Push content", "name1":"value1", "badge":"150"}
}'

Notification message:

curl -i -H 'Content-type: application/json' -H 'Authorization: key=#####KEY#####' -XPOST https://fcm.googleapis.com/fcm/send -d '{
  "registration_ids":["####DEVICE_ID#####"],
  "notification": {"title":"Push Title", "body":"Push content"}
}'

using PHP

To test your app you can use this PHP script to send messages to the device/topic:

<?php
    $url = 'https://fcm.googleapis.com/fcm/send';

    $fields = [
        'to' => '/topics/testTopic', // or device token
        'notification' => [
            'title' => 'TiFirebaseMessaging',
            'body' => 'Message received',
            "badge"=> 1,
        ],
        'data' => [
            'key1' => 'value1',
            'key2' => 'value2'
        ]
    ];

    $headers = [
        'Authorization: key=SERVER_ID_FROM_FIREBASE_SETTIGNS_CLOUD_MESSAGING', 'Content-Type: application/json'
    ];
    $ch = curl_init();

    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_POST, true);
    curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($fields));

    $result = curl_exec($ch);

    echo $result;
    curl_close($ch);
?>

Run it locally with php filelane.php or put it on a webserver where you can execute PHP files.

extended PHP Android example

<?php $url = 'https://fcm.googleapis.com/fcm/send';

    $fields = array (
        'to' => "TOKEN_ID",             // specific ID
        // 'to' => "/topics/test",      // topic
        // 'notification' => array (
        //   "title" => "TiFirebaseMessaging",
        //   "body" => "Message received πŸ“±πŸ˜‚",
        //   "timestamp"=>date('Y-m-d G:i:s'),
        //   "android_channel_id" => "my_other_channel"
        // ),
        'data' => array(
            "test1" => "value1",
            "test2" => "value2",
            "timestamp"=>date('Y-m-d G:i:s'),
            "title" => "title",
            "message" => "message",
            "big_text"=>"big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text big text even more text ",
            "big_text_summary"=>"big_text_summary",
            "icon" => "http://via.placeholder.com/150x150",
            "image" => "http://via.placeholder.com/350x150",	// won't show the big_text
            "force_show_in_foreground"=> true,
            "color" => "#ff6600",
            "channelId" => "default"	// or a different channel
        )
    );

    $headers = array (
        'Authorization: key=API_KEY',
        'Content-Type: application/json'
    );

    $ch = curl_init ();
    curl_setopt ( $ch, CURLOPT_URL, $url );
    curl_setopt ( $ch, CURLOPT_POST, true );
    curl_setopt ( $ch, CURLOPT_HTTPHEADER, $headers );
    curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true );
    curl_setopt ( $ch, CURLOPT_POSTFIELDS, json_encode($fields));

    $result = curl_exec ( $ch );
    echo $result."\n";
    curl_close ( $ch );
?>

Parse

You can use Parse with this module: https://github.com/timanrebel/Parse/pull/59 in combination with Firebase. You include and configure both modules and send your deviceToken to the Parse backend.

If you send a push over e.g. Sashido you can either send a normal text or a json with:

{"alert":"test from sashido", "text":"test"}

With the JSON you can set a title/alert and the text of the notification.

Build

iOS

cd ios
ti build -p ios --build-only

Android

cd android
ti build -p android --build-only

(c) 2017-Present by Hans KnΓΆchel & Michael Gangolf

Open Source Agenda is not affiliated with "Titanium Firebase Cloud Messaging" Project. README Source: hansemannn/titanium-firebase-cloud-messaging

Open Source Agenda Badge

Open Source Agenda Rating