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

Status
Not open for further replies.

Clarification: The nice thing about FCM is that your app doesn't need to run in order to receive messages. The FirebaseMessaging service 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 Service named FirebaseMessaging to your app (must be this name):
B4X:
Sub Process_Globals
   Private fm As FirebaseMessaging
End Sub

Sub Service_Create
   fm.Initialize("fm")
End Sub

Public Sub SubscribeToTopics
   fm.SubscribeToTopic("general") 'you can subscribe to more topics
End Sub

Sub Service_Start (StartingIntent As Intent)
   If StartingIntent.IsInitialized Then fm.HandleIntent(StartingIntent)
   Sleep(0)
   Service.StopAutomaticForeground 'remove if not using B4A v8+.
End Sub

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

Sub Service_Destroy

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: 2,122
Last edited:

An Schi

Well-Known Member
Licensed User
I just made two EditText for title and body and a button. In the button_click i pasted the send code given here and adjusted it to my keys. That's it...
Oh and don't forget to check the needed libraries.
 

Alpandino

Member
Licensed User
See the code in the first post. Just don't call StopMessageLoop in JobDone.
What is "StopMessageLoop" in the JobDone? I don't see it... and I have coded an App in B4A that send and receive msgs using Firebase.
I'm missing something?
 

luke2012

Well-Known Member
Licensed User
See the code in the first post. Just don't call StopMessageLoop in JobDone.

My target is to call the SendMessage sub from a B4J UI App.
if I try to do this I got an error that says "java.lang.RuntimeException: StartMessageLoop should only be called in non-UI applications."

B4X:
Sub AppStart (Form1AsForm, Args() AsString)
     SendMessage("general", "Title", "message...")
     StartMessageLoop
End Sub

@Erel ignore this post. I got it...
I did a better check of the code and I understood what you told me ;)
 

Claudio Oliveira

Active Member
Licensed User
Hi,
Is it possible to send JSON arrays through FCM?
I've been searching this forum and FCM documentation but haven't found any information on this whatsoever.

I've made an adaptation of Erel's code on Post #1

I'm trying to post this message through FCM:
B4X:
{"to":"XXXXXXX","data":[{"col":"Testing","value":"Test #1"},{"col":"Testing","value":"Test #2"},{"col":"Testing","value":"Test #3"}]}

JSONGenerator pretty string is this:
B4X:
{
   "to":XXXXXXX",
   "data": [
      {
         "col": "Testing",
         "value": "Test #1"
      },
      {
         "col": "Testing",
         "value": "Test #2"
      },
      {
         "col": "Testing",
         "value": "Test #3"
      }
   ]
}

This is my code:

B4X:
Private Sub SendMessage(Topic As String, ColName As String, ColValue As String)
    Dim m As Map
    Dim Job As HttpJob
    Dim data As Map
    Dim l As List
   
    l.Initialize
   
    Job.Initialize("fcm", Me)
    m = CreateMap("to": *****)
    For j=1 To 3
        data = CreateMap("col": ColName, "value": ColValue & J)
        l.Add(data)
    Next
    m.Put("data", l)
    Dim jg As JSONGenerator
    jg.Initialize(m)   
    Log(jg.ToPrettyString(3))
    Log(jg.ToString)
    Job.PostString("https://fcm.googleapis.com/fcm/send", jg.ToString)
    Job.GetRequest.SetContentType("application/json;charset=UTF-8")
    Job.GetRequest.SetHeader("Authorization", "key=" & "*****")

End Sub


Sub JobDone(job As HttpJob)
    Log(job)
    If job.Success Then
        Log(job.GetString)
    End If
    job.Release
End Sub

FCM returns this error message:

Field "data" must be a JSON array: [{"col":"Testing","value":"Test #1"},{"col":"Testing","value":"Test #2"},{"col":"Testing","value":"Test #3"}]
[errormessage=Bad Request, httputils2service=null, jobname=fcm
, password=, req=anywheresoftware.b4a.http.HttpClientWrapper$HttpUriRequestWrapper@3b5da6c, success=false
, tag=java.lang.Object@8831835, target=class com.ceosoft.tstlist.main, taskid=1
, username=]

As far as I know and according to JSON documentation, my array is absolutely fine.
Any ideas?

Thanks
 

biometrics

Active Member
Licensed User
I've followed the instructions here (https://www.b4x.com/android/forum/threads/integrating-firebase-services.67692/) to register a project with Firebase and have copied the manifest code. I haven't added any of the code in the OP yet.

When I compile my project I now get the following error, please advise:

B4X:
AndroidManifest.xml:35: error: Error: No resource found that matches the given name (at 'value' with value '@integer/google_play_services_version').
AndroidManifest.xml:123: error: Error: No resource found that matches the given name (at 'theme' with value '@style/Theme.IAPTheme').

I have the following added in SDK Manager:

- Android Support Repository
- Google Repository
- Google Play Services (wasn't sure so added this just in case)
 

biometrics

Active Member
Licensed User
Use this tool to install the latest SDK with all recommended items: https://www.b4x.com/android/forum/threads/80090/#content

I followed the instructions and downloaded the recommended options. (I renamed my current C:\Android directory and started a new one). I updated the paths (I was at SDk platform 25 and your tool recommended 24).

I'm still getting this error when trying to compile (note I only included the manifest changes, have not included any code yet):

B4X:
AndroidManifest.xml:35: error: Error: No resource found that matches the given name (at 'value' with value '@integer/google_play_services_version').
AndroidManifest.xml:123: error: Error: No resource found that matches the given name (at 'theme' with value '@style/Theme.IAPTheme').

My manifest file:

B4X:
'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="14"/>
<supports-screens android:largeScreens="true"
  android:normalScreens="true"
  android:smallScreens="true"
  android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
SetApplicationAttribute(android:theme, "@android:style/Theme.Holo")
'End of default text.

'************ Google Play Services Base ************
AddApplicationText(
  <activity android:name="com.google.android.gms.common.api.GoogleApiActivity"
  android:theme="@android:style/Theme.Translucent.NoTitleBar"
  android:exported="false"/>
  <meta-data
  android:name="com.google.android.gms.version"
  android:value="@integer/google_play_services_version" />
)
'************ Google Play Services Base (end) ************

'************ Firebase Base ************
CreateResourceFromFile("google-services", "google-services.json")
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.WAKE_LOCK)
AddPermission(com.google.android.c2dm.permission.RECEIVE)
AddPermission(${applicationId}.permission.C2D_MESSAGE)
AddManifestText( <permission android:name="${applicationId}.permission.C2D_MESSAGE"
  android:protectionLevel="signature" />)
AddApplicationText(
<receiver
  android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
  android:enabled="true">
  <intent-filter>
  <action android:name="com.google.android.gms.measurement.UPLOAD"/>
  </intent-filter>
  </receiver>

  <service
  android:name="com.google.android.gms.measurement.AppMeasurementService"
  android:enabled="true"
  android:exported="false"/>
  <provider
  android:authorities="${applicationId}.firebaseinitprovider"
  android:name="com.google.firebase.provider.FirebaseInitProvider"
  android:exported="false"
  android:initOrder="100" />
  <receiver
  android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
  android:enabled="true">
  <intent-filter>
  <action android:name="com.google.android.gms.measurement.UPLOAD"/>
  </intent-filter>
  </receiver>

  <service
  android:name="com.google.android.gms.measurement.AppMeasurementService"
  android:enabled="true"
  android:exported="false"/>
  <receiver
  android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
  android:exported="true"
  android:permission="com.google.android.c2dm.permission.SEND" >
  <intent-filter>
  <action android:name="com.google.android.c2dm.intent.RECEIVE" />
  <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
  <category android:name="${applicationId}" />
  </intent-filter>
  </receiver>
  <receiver
  android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
  android:exported="false" />


  <service
  android:name="com.google.firebase.iid.FirebaseInstanceIdService"
  android:exported="true">
  <intent-filter android:priority="-500">
  <action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
  </intent-filter>
  </service>
)
'************ Firebase Base (end) ************

'************ Firebase Notifications ************
AddApplicationText(
  <service
  android:name="com.google.firebase.messaging.FirebaseMessagingService"
  android:exported="true">
  <intent-filter android:priority="-500">
  <action android:name="com.google.firebase.MESSAGING_EVENT" />
  </intent-filter>
  </service>
  <service android:name="anywheresoftware.b4a.objects.FirebaseNotificationsService">
  <intent-filter>
  <action android:name="com.google.firebase.MESSAGING_EVENT"/>
  </intent-filter>
  </service>
)
'************ Firebase Notifications (end)************

'************ Firebase Analytics ************
AddApplicationText(
  <receiver
  android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
  android:enabled="true">
  <intent-filter>
  <action android:name="com.google.android.gms.measurement.UPLOAD"/>
  </intent-filter>
  </receiver>

  <service
  android:name="com.google.android.gms.measurement.AppMeasurementService"
  android:enabled="true"
  android:exported="false"/>

  <service android:name="com.google.firebase.crash.internal.service.FirebaseCrashReceiverService"
  android:process=":background_crash"/>

  <service android:name="com.google.firebase.crash.internal.service.FirebaseCrashSenderService"
  android:process=":background_crash"/>
)
'************ Firebase Analytics (end) ************

'************ Firebase Ads ************
AddApplicationText(
  <activity
  android:name="com.google.android.gms.ads.AdActivity"
  android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"
  android:theme="@android:style/Theme.Translucent" />
  <activity android:name="com.google.android.gms.ads.purchase.InAppPurchaseActivity"
  android:theme="@style/Theme.IAPTheme"/>
)
'************ Firebase Ads (end) ************

'************ Firebase Auth ************
AddApplicationText(
  <activity android:name="com.google.android.gms.auth.api.signin.internal.SignInHubActivity"
  android:theme="@android:style/Theme.Translucent.NoTitleBar"
  android:excludeFromRecents="true"
  android:exported="false" />

  <service
  android:name="com.google.android.gms.auth.api.signin.RevocationBoundService"
  android:exported="true"
  android:permission="com.google.android.gms.auth.api.signin.permission.REVOCATION_NOTIFICATION" />
)
'************ Firebase Auth (end) ************

My paths:

Capture.JPG
 

biometrics

Active Member
Licensed User
Can you upload your project?

See attached zip file.

I've created a new project and all I did is add the manifest entries from https://www.b4x.com/android/forum/threads/integrating-firebase-services.67692/ and copied the google-services.json to the project folder (where the .b4a file is located). I've got a clean & recommended Android installation as per https://www.b4x.com/android/forum/t...ewer-versions-of-android-tools.80090/#content. I'm running B4A 7.01.

Still gives me this error:

B4X:
AndroidManifest.xml:28: error: Error: No resource found that matches the given name (at 'value' with value '@integer/google_play_services_version').
AndroidManifest.xml:116: error: Error: No resource found that matches the given name (at 'theme' with value '@style/Theme.IAPTheme').

I've changed the unique ids in google-services.json for the zip upload (it gives the error with my ids and with the anonymised ids).
 

Attachments

  • Test.zip
    17.2 KB · Views: 144

DonManfred

Expert
Licensed User
all I did is add the manifest entries from https://www.b4x.com/android/forum/threads/integrating-firebase-services.67692/ and copied the google-services.json to the project folder
Make sure you are using alle the firebase libraries which you are including in the manifest.
For example you are including the manifestentry for Firebasemessaging but you are NOT using the lib and even you did not added the lib to your project.
You are not adding firebase messaging (and setup it correctly in code)
You are not using Firebase Auth, not firebase Anylytics, not Firebase Ads (the ladder is using the Theme.IAPTheme (second error)

Add Firebase Auth library to your project.

Use this manifestcontent

'This code will be applied to the manifest file during compilation.
'You do not need to modify it in most cases.
'See this link for for more information: https://www.b4x.com/forum/showthread.php?p=78136
AddManifestText(
<uses-sdk android:minSdkVersion="5" android:targetSdkVersion="19"/>
<supports-screens android:largeScreens="true"
android:normalScreens="true"
android:smallScreens="true"
android:anyDensity="true"/>)
SetApplicationAttribute(android:icon, "@drawable/icon")
SetApplicationAttribute(android:label, "$LABEL$")
'End of default text.


'************ Google Play Services Base ************
AddApplicationText(
<activity android:name="com.google.android.gms.common.api.GoogleApiActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="false"/>
<meta-data
android:name="com.google.android.gms.version"
android:value="@Integer/google_play_services_version" />
)
'************ Google Play Services Base (end) ************

'************ Firebase Base ************
CreateResourceFromFile("google-services", "google-services.json")
AddPermission(android.permission.ACCESS_NETWORK_STATE)
AddPermission(android.permission.INTERNET)
AddPermission(android.permission.WAKE_LOCK)
AddPermission(com.google.android.c2dm.permission.RECEIVE)
AddPermission(${applicationId}.permission.C2D_MESSAGE)
AddManifestText( <permission android:name="${applicationId}.permission.C2D_MESSAGE"
android:protectionLevel="signature" />)
AddApplicationText(
<receiver
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
android:enabled="true">
<intent-filter>
<action android:name="com.google.android.gms.measurement.UPLOAD"/>
</intent-filter>
</receiver>

<service
android:name="com.google.android.gms.measurement.AppMeasurementService"
android:enabled="true"
android:exported="false"/>
<provider
android:authorities="${applicationId}.firebaseinitprovider"
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:exported="false"
android:initOrder="100" />
<receiver
android:name="com.google.android.gms.measurement.AppMeasurementReceiver"
android:enabled="true">
<intent-filter>
<action android:name="com.google.android.gms.measurement.UPLOAD"/>
</intent-filter>
</receiver>

<service
android:name="com.google.android.gms.measurement.AppMeasurementService"
android:enabled="true"
android:exported="false"/>
<receiver
android:name="com.google.firebase.iid.FirebaseInstanceIdReceiver"
android:exported="true"
android:permission="com.google.android.c2dm.permission.SEND" >
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE" />
<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
<category android:name="${applicationId}" />
</intent-filter>
</receiver>
<receiver
android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver"
android:exported="false" />


<service
android:name="com.google.firebase.iid.FirebaseInstanceIdService"
android:exported="true">
<intent-filter android:priority="-500">
<action android:name="com.google.firebase.INSTANCE_ID_EVENT" />
</intent-filter>
</service>
)
'************ Firebase Base (end) ************

It compiles fine here
 

biometrics

Active Member
Licensed User
Make sure you are using alle the firebase libraries which you are including in the manifest.
For example you are including the manifestentry for Firebasemessaging but you are NOT using the lib and even you did not added the lib to your project.
You are not adding firebase messaging (and setup it correctly in code)
You are not using Firebase Auth, not firebase Anylytics, not Firebase Ads (the ladder is using the Theme.IAPTheme (second error)

Add Firebase Auth library to your project.

Use this manifestcontent



It compiles fine here

Thanks, I got it working now!

It wasn't clear to me that some of the manifest entries are optional. I've added these:

Google Play Services Base
Firebase Base
Firebase Notifications

And I only noticed now that the library was in this thread. Added it and it's working. Thanks Manfred! :)
 
Status
Not open for further replies.
Top