B4A Library FirebaseNotifications - Push messages / Firebase Cloud Messaging (FCM)

Status
Not open for further replies.
Updated tutorial: https://www.b4x.com/android/forum/threads/b4x-firebase-push-notifications-2023.148715/


Clarification: The nice thing about FCM is that your app doesn't need to run in order to receive messages. The FirebaseMessaging receiver will be started by the OS when a new message arrives.
It is not a real issue, but if the user closed your app with a "force close" from the device settings then messages will not arrive, until the app is started again.
On some misbehaving devices, killing the app with a swipe will also cause messages not to arrive, until the app is started again.

Firebase Cloud Messaging service is a layer above Google Cloud Messaging service.
It makes it simple to add support for push messages.

Sending messages is done with a simple HTTP request. It is also possible to send message from the Firebase console, though it is not very useful and is actually more complicated than using the REST api.

1. The first step is to follow the Firebase integration tutorial:
https://www.b4x.com/android/forum/threads/integrating-firebase-services.67692/

Make sure to add the Notifications snippet.
You should also reference FirebaseAnalytics

2. Add a Receiver named FirebaseMessaging to your app (must be this name):
B4X:
Sub Process_Globals
    Private fm As FirebaseMessaging
End Sub

Private Sub Receiver_Receive (FirstTime As Boolean, StartingIntent As Intent)
    If FirstTime Then
        fm.Initialize("fm")
    End If
    fm.HandleIntent(StartingIntent)
End Sub

Public Sub SubscribeToTopics
    fm.SubscribeToTopic("general")
End Sub

Sub fm_MessageArrived (Message As RemoteMessage)
    Log("Message arrived")
    Log($"Message data: ${Message.GetData}"$)
    Dim n2 As Notification
    n2.Initialize2(n2.IMPORTANCE_DEFAULT)
    n2.Icon = "icon"
    n2.SetInfo(Message.GetData.Get("title"), Message.GetData.Get("body"), Main)
    n2.Notify(1)  
End Sub

Sub fm_TokenRefresh (Token As String)
    Log("TokenRefresh: " & Token)
End Sub

fm_MessageArrived will be raised whenever a message is received. In this case we show a notification. You can do whatever you need.

We call SubscribeToTopics from the starter service to make sure that the app will be subscribed when it starts:
B4X:
'Starter service
Sub Process_Globals

End Sub

Sub Service_Create
   CallSubDelayed(FirebaseMessaging, "SubscribeToTopics")
End Sub

Now we can send messages to a "topic" and all the subscribed devices will receive it.

See the code in the attached B4J tool. Note that the API_KEY should be set in the B4J code. It shouldn't be distributed in your app.

API_KEY is the server key from:

SS-2017-04-07_08.10.47.png


A simple non-ui B4J program is attached.

Note that messages sent from Firebase Console will not arrive in some cases. Use the B4J code to test it.
 

Attachments

  • B4J-SendingTool.zip
    1.1 KB · Views: 1,123
Last edited:

scsjc

Well-Known Member
Licensed User
Longtime User
Hello,
Someone know why the "Console Firebase Notification" don't work with a notification with a same way going to "fm_MessageArrived"

because i test, and always when use a Console have a notification from app but dont work the code inside "fm_MessageArrived"
and when send a message via PHP or via ANDROID, work perfectly.

I want work with a console because i have %clicks send number notifications bla bla bla

thanks!!!
 

scsjc

Well-Known Member
Licensed User
Longtime User
You should use the B4J or B4A code posted in the first post. You can send whichever data you need.

yes ... i now...i use.... but the nice from Console is the Statistics ;) for that i want i try to work with console
 

An Schi

Well-Known Member
Licensed User
It is a limitation of the console. Somehow pushes from the console don't start your service like the ones from the API do. It is documented in stackoverflow, sorry i don't have the link at hand (but i posted it allready some time ago to another thread).
 

iCAB

Well-Known Member
Licensed User
Longtime User
Hi All

Since all what it takes is a simple HTTP request to send a message to a topic, is it safe to assume that we don't need a server at all to create a messaging platform between mobile devices provided that each of the devices knows the server API Key.

Something similar to this
1. At startup: a mobile device retrieves encrypted API key from server
2. Mobile devices subscribe to unique topics for receiving private messages ( for example: user's email address )
3. Mobile device subscribe to common topics for receiving broadcast messages
4. A mobile device can send a broadcast or a private message to another device by simply sending to known topics

One more related question:
Is there a limit to the number of topics a client can subscribe to?


Please comment
Thanks in advance
 
Last edited:

Alpandino

Member
Licensed User
I did a lot of test. I disabled the developer mode, I disconnected the usb cable, but occasionally my device becomes unable to receive message. I put a toastmessage inside FirebaseMessaging service, as first line of Service_Create and Service_Start:

B4X:
Sub Service_Create
  
    fm.Initialize("fm")
    ToastMessageShow("Ho appena inizializzato FM", False)
    OtherUser.Initialize
    mapNewusers.Initialize
End Sub

Public Sub SubscribeToTopics(topic As String)
   'fm.SubscribeToTopic("general") 'you can subscribe to more topics
   fm.SubscribeToTopic(topic)
   Log("Mi sono registrato a "&topic)
End Sub

Public Sub UnsubscribeToTopics(topic As String)
    fm.UnsubscribeFromTopic(topic)
End Sub

Sub Service_Start (StartingIntent As Intent)
    'Log("StartingIntent FM: "&StartingIntent)
    ToastMessageShow("FM is started",False)
      If fm.HandleIntent(StartingIntent) Then Return
End Sub

When I'm unable to receive a message, after someone sends me a message, I see that the FirebaseMessaging service starts looking into the application management of android, but I don't see any toastmessage.
There is no a timing, sometimes it happens in few minutes, sometimes after 2 hours.
This behavior seems don't appears in other devices. I tested other 2 devices and all is working fine, for the moment.
Please, help me, I don't have other ideas.... :-(

Hi all, I dealt the problem and I finally understood the root cause. I want to share the solution with you: the problem was the fact that I made the Starter service as sticky. If I remove the stickyness of starter service everything works really fine, I'm able to receive each message, at any time, also if I stop manually my app.
I don't understand why, but this is the solution
I hope this can be useful for other users

Bye
 

iCAB

Well-Known Member
Licensed User
Longtime User
Hi All

I am trying to make sure I understand the exact startup sequence
In the first post

We call SubscribeToTopics from the starter service to make sure that the app will be subscribed when it starts:

How do we know that by the time SubscribeToTopics is called, fm is already initialized and registered, and if not what is the Proper way of checking this.

Thanks
 
Last edited:

alimanam3386

Active Member
Licensed User
Longtime User
Hi All

Since all what it takes is a simple HTTP request to send a message to a topic, is it safe to assume that we don't need a server at all to create a messaging platform between mobile devices provided that each of the devices knows the server API Key.

Something similar to this
1. At startup: a mobile device retrieves encrypted API key from server
2. Mobile devices subscribe to unique topics for receiving private messages ( for example: user's email address )
3. Mobile device subscribe to common topics for receiving broadcast messages
4. A mobile device can send a broadcast or a private message to another device by simply sending to known topics

One more related question:
Is there a limit to the number of topics a client can subscribe to?


Please comment
Thanks in advance
Hi

How can you make sure that if we put the server key in client side it will be safe ? If we encrypt it ( server key ) we have to decrypt it in client side again !
 

iCAB

Well-Known Member
Licensed User
Longtime User
How can you make sure that if we put the server key in client side it will be safe ? If we encrypt it ( server key ) we have to decrypt it in client side again !

In my case, I decrypt the key on the client side and assign it to a global variable( never saved to a file ). You can address the issue in many ways, it all depends on how much security you need.
 

alimanam3386

Active Member
Licensed User
Longtime User
In my case, I decrypt the key on the client side and assign it to a global variable( never saved to a file ). You can address the issue in many ways, it all depends on how much security you need.

Hi
But if you decrypt the server key in client side your users can get it easy ! ( by decrpt the apk )
 

iCAB

Well-Known Member
Licensed User
Longtime User
Hi
But if you decrypt the server key in client side your users can get it easy ! ( by decrpt the apk )

To the best of my knowledge, if the key (encrypted/decrypted), is not being stored anywhere in a file, it will be extremely hard to get, unless you are talking about decompiling the code and figuring out the encryption algorithm used.

I don't have in depth knowledge of the android OS and the compiler etc.. so I will leave that to one of the experts to comment on.
 

alimanam3386

Active Member
Licensed User
Longtime User
To the best of my knowledge, if the key (encrypted/decrypted), is not being stored anywhere in a file, it will be extremely hard to get, unless you are talking about decompiling the code and figuring out the encryption algorithm used.

I don't have in depth knowledge of the android OS and the compiler etc.. so I will leave that to one of the experts to comment on.

Yes exactly , if we decompile the apk we can get the algorithm of encryption/decryption and it's equals with server key.
 

iCAB

Well-Known Member
Licensed User
Longtime User
Yes exactly , if we decompile the apk we can get the algorithm of encryption/decryption and it's equals with server key.

If you can do that, then nothing prevents you from achieving the same results regardless of where the key resides (on the server or in the app ). As far as I know there are only couple of ways of doing this:

1. Directly from the app using the key ( note that in my proposed solution, the key is exchanged in encrypted format and using a secure handshake that authenticate the client. The key resides in some global variable )

2. issue a message to the server, to forward the message (one way or the other ) to the addressed device.

if you can decompile the code neither one is secure.

Perhaps Erel or one of the security experts on the site can elaborate
 
Last edited:

iCAB

Well-Known Member
Licensed User
Longtime User
Strings that are set in Sub Process_Globals will be obfuscated. It adds some protection.

Here is what I am trying to understand based on the conversation above and based on the fact that someone can decompile the apk in an attempt to hack the system.

1. In my solution, the server API key is not part of the apk at all
2. The key is exchanged between the server and the client in encrypted format and using a secure http handshake (session keys, message signing etc.. )
3. The descrypted key is stored in a Global variable and not in a file


1. Is it possible for someone to get access to the key based on the above? I am not talking about decrypting the key at this point. Just accessing the variable holding the key in memory

2. assuming that someone is attempting to hack the system, wouldn't it be easier to figure out a way to send messages to the small B4J server than figuring out the encryption used to retrieve the key

Please comment
 
Status
Not open for further replies.
Top