Android Tutorial [B4X] Firebase Push Notifications 2023+

Status
Not open for further replies.
Google has deprecated the previous method of sending push notifications. It will stop working on June 2024.
If you have an already working solution then you only need to update the B4J side. Do make sure that you switched from the service to a receiver on B4A.
The new API is (confusingly) named FCM API v1.

B4J

1. Make sure that the new API is enabled as shown in this screenshot:

1687781823038.png


2. Project settings - Service accounts - Generate new private sign key. The browser will download a json file. This file includes a private key that gives access to your firebase account. Keep it secured.
This file is used by the sending tool.
3. Find the project id under the General tab.
4. Download the dependencies and unpack in B4J additional libraries folder: https://www.b4x.com/b4j/files/b4j-push-deps.zip
5. Download B4J-SendingTool.zip. Update ProjectId and ServiceAccountFilePath.

B4A

1. Follow the generic Firebase integration tutorial: https://www.b4x.com/android/forum/threads/integrating-firebase-services.67692/
Download google-services.json and put it in the project folder.
2. See the attached project.
3. The notification is created in FirebaseMessaging receiver. Note that in debug mode, it will only work while the app is running.

B4i

1. Follow the configuration instructions: https://www.b4x.com/android/forum/t...h-messages-server-not-required.68645/#content (ignore the code references).
2. Download the attached project. Note the two subs added there (RemoteNotification and PushToken).

Notes

1. In iOS the notification popup only appears while the app is not in the foreground. You can UserNotificationCenter to change this behavior (search and you will find).
2. FirebaseMessaging module isn't shared between the two projects. It is different in B4A and B4i.
3. The message payload can be customized in many ways. API reference: https://firebase.google.com/docs/reference/fcm/rest/v1/projects.messages
 

Attachments

  • PushClients.zip
    16 KB · Views: 528
  • B4J-SendingTool.zip
    1.8 KB · Views: 313
Last edited:

derez

Expert
Licensed User
Longtime User
The question isn't clear enough. You cannot receive Firebase notifications on the desktop. You can have a Windows client that is connected to the B4J server and shows notifications.
Because I saw this in "Google cloud messaging" site :
An Apple, Android, or web (JavaScript) client app that receives messages via the corresponding platform-specific transport service.
 

derez

Expert
Licensed User
Longtime User
The instructions are here: https://firebase.google.com/docs/cloud-messaging/js/client
Not trivial but possible with a server and through a browser. Not directly.

If you have your own server than their are simpler solutions.
My solution:
The sending app send a MQTT message in parallel to sending the FB message, to the cloud MQTT that I use already.
A small b4j app runs on the PC and listens to MQTT. Its window is small and out of the screen until a msg is received, then it is shown. Hiding it again is either by the user or by timer.

1689580268252.png
 

Sandman

Expert
Licensed User
Longtime User
Apologies for going slightly off-topic.
A small b4j app runs on the PC and listens to MQTT. Its window is small and out of the screen until a msg is received, then it is shown. Hiding it again is either by the user or by timer.
This thread might be relevant for you:
 

derez

Expert
Licensed User
Longtime User
Apologies for going slightly off-topic.

This thread might be relevant for you:
Thanks for the pointer but my app seems to me simpler and without any additional libraries or dependencies. I may work a little on the look of it but thats it.
 

GraemeW

Member
Licensed User
Bit slow here so just checking .. my app sends notifications to all its peers by the FCM legacy API via a php script on a shared hosted Unix server. The script stores the API key.

For the new API I'm planning to implement a version of the B4J sending tool script on the Unix server - with the .json file stored on a Unix path. My app will then call the remote js when it needs to send notifications. Does this make sense?
 

Andrew (Digitwell)

Well-Known Member
Licensed User
Longtime User
Bit slow here so just checking .. my app sends notifications to all its peers by the FCM legacy API via a php script on a shared hosted Unix server. The script stores the API key.

For the new API I'm planning to implement a version of the B4J sending tool script on the Unix server - with the .json file stored on a Unix path. My app will then call the remote js when it needs to send notifications. Does this make sense?
If you are using PHP on a server why not use the solution given by @FrostCodes in post 8?
 

GraemeW

Member
Licensed User
If you are using PHP on a server why not use the solution given by @FrostCodes in post 8?
I'm still figuring it out but I'm still in dummy class for php - first attempt yielded "The registration token is not a valid FCM registration token" šŸ˜¬
 

IdasI4A

Active Member
Licensed User
Longtime User
B4J-SendingTools works perfectly in the IDE, but when I make a standalone package, and run the package I get the error:

main._gettokenvalue (java line: -1)
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at b4j/anywheresoftware.b4j.object.JavaObject.RunMethod(Unknown Source)
at b4j/anywheresoftware.b4j.object.JavaObject.RunMethodJO(Unknown Source)
at b4j/idasi.enviamensajeferro.main._gettokenvalue(Unknown Source)
at b4j/idasi.enviamensajeferro.main$ResumableSub_Send.resume(Unknown Source)
at b4j/idasi.enviamensajeferro.main._send(Unknown Source)
at b4j/idasi.enviamensajeferro.main._appstart(Unknown Source)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at b4j/anywheresoftware.b4a.BA.raiseEvent2(Unknown Source)
at b4j/anywheresoftware.b4a.BA.raiseEvent(Unknown Source)
at b4j/idasi.enviamensajeferro.main.main(Unknown Source)
Caused by: java.util.ServiceConfigurationError: com.google.auth.http.HttpTransportFactory: module b4j does not declare `uses`
at java.base/java.util.ServiceLoader.fail(Unknown Source)
at java.base/java.util.ServiceLoader.checkCaller(Unknown Source)
at java.base/java.util.ServiceLoader.<init>(Unknown Source)
at java.base/java.util.ServiceLoader.load(Unknown Source)
at b4j/com.google.auth.oauth2.OAuth2Credentials.getFromServiceLoader(Unknown Source)
at b4j/com.google.auth.oauth2.ServiceAccountCredentials.<init>(Unknown Source)
at b4j/com.google.auth.oauth2.ServiceAccountCredentials.fromPkcs8(Unknown Source)
at b4j/com.google.auth.oauth2.ServiceAccountCredentials.fromJson(Unknown Source)
at b4j/com.google.auth.oauth2.GoogleCredentials.fromStream(Unknown Source)
at b4j/com.google.auth.oauth2.GoogleCredentials.fromStream(Unknown Source)
... 17 more
Can you tell me what I'm doing wrong?
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
Updated the sending tool with a single addition:
B4X:
#PackagerProperty: AdditionalModuleInfoString = uses com.google.auth.http.HttpTransportFactory;

With this line the standalone packager will work.

Tip: if you want to put the json file in the Files folder to simplify packaging then change GetTokenValue to:
B4X:
Private Sub GetTokenValue (Dir As String, FileName As String) As String
    Dim GoogleCredentials As JavaObject
    GoogleCredentials.InitializeStatic("com.google.auth.oauth2.GoogleCredentials")
    Dim Credentials As JavaObject = GoogleCredentials.RunMethodJO("fromStream", Array(File.OpenInput(Dir, FileName))) _
        .RunMethod("createScoped", Array(Array As String("https://www.googleapis.com/auth/firebase.messaging")))
    Credentials.RunMethod("refreshIfExpired", Null)
    Return Credentials.RunMethodJO("getAccessToken", Null).RunMethod("getTokenValue", Null)
End Sub
 

IdasI4A

Active Member
Licensed User
Longtime User
Updated the sending tool with a single addition:
B4X:
#PackagerProperty: AdditionalModuleInfoString = uses com.google.auth.http.HttpTransportFactory;

With this line the standalone packager will work.

Tip: if you want to put the json file in the Files folder to simplify packaging then change GetTokenValue to:
B4X:
Private Sub GetTokenValue (Dir As String, FileName As String) As String
    Dim GoogleCredentials As JavaObject
    GoogleCredentials.InitializeStatic("com.google.auth.oauth2.GoogleCredentials")
    Dim Credentials As JavaObject = GoogleCredentials.RunMethodJO("fromStream", Array(File.OpenInput(Dir, FileName))) _
        .RunMethod("createScoped", Array(Array As String("https://www.googleapis.com/auth/firebase.messaging")))
    Credentials.RunMethod("refreshIfExpired", Null)
    Return Credentials.RunMethodJO("getAccessToken", Null).RunMethod("getTokenValue", Null)
End Sub
Thank you, thank you very much. It works perfectly
 

dieterp

Active Member
Licensed User
Longtime User
At some point while running the B4J-SendingTool app I get the error below. Should we be handling token refreshing ourselves or is this supposed to be handled in the existing code (It looks like the code should be handling it)

UPDATE: I have implemented a timer to call GetTokenValue(ServiceAccountFilePath) every 30 minutes which has resolved the issue (The token expires after 60 min). It's worth noting that I modified the code into a UI based app from the Non-UI example. I'm not sure if this perhaps changes the behavior of the token refreshing in a different manner

1690638670503.png
 
Last edited:
Status
Not open for further replies.
Top