Android Question Save Firebase Notifications

Alvsky

Member
Licensed User
Longtime User
Hi all,
I created a small app which I use to send notifications to users.
My idea is that after notification is received I store it in a database so user can access it all the time.

Notifications is sent from same application but sending feature is "hidden" and protected so only I know how to send a message.
Everything works fine except when application is not running.
When I send notification, it arrives to another phone and I CAN see it and read it but it is not saved in DB.
In all other cases (when app is started) I can save the message.

This is what happens in log after I click on notification if application is closed:

B4X:
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
/storage/emulated/0/Android/data/com.vitka.FloorFitness/files
** Activity (main) Resume **
Main-Resume - poruka upisana prije: false
Main-Resume - poruka upisana poslije: false
*** Service (firebasemessaging) Create ***
** Service (firebasemessaging) Start **

'HERE SHOULD BE "Message arrived" LOG ENTRY but it's not if app is closed

I guess that problem is because Activity_Resume starts before firebasemessaging but I don't know how to fix it.



and here is FirebaseMessaging Service code


B4X:
#Region  Service Attributes
   #StartAtBoot: False
#End Region

Sub Process_Globals
   Private fm As FirebaseMessaging
'   Dim var As String
   Dim varTitle, varBody As String
   Dim dbS As SQL
   Dim ruta As String
   
End Sub

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

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

Sub Service_Start (StartingIntent As Intent)
   If StartingIntent.IsInitialized And fm.HandleIntent(StartingIntent) Then Return
End Sub

Sub fm_MessageArrived (Message As RemoteMessage)
   Log("Message arrived")

   If File.ExternalWritable Then
     ruta = File.DirDefaultExternal
   Else
     ruta = File.DirInternal
   End If
   Log($"Message data: ${Message.GetData}"$)
   
   dbS.Initialize(ruta, "FloorFitness.db", True)
   dbS.ExecNonQuery2("INSERT INTO messages VALUES (?, ?, ?, ?, ?)", Array As Object(Null, DateTime.Date(DateTime.Now),Message.GetData.Get("title"),Message.GetData.Get("body"), "0"))
'   Main.porukaUpisana = True
   
   
   Dim n As Notification
   n.Initialize
   n.Icon = "icon"
   n.SetInfo(Message.GetData.Get("title"), Message.GetData.Get("body"), Main)'Otvara Main activity
   
   n.Notify(1)

   Log("Poruka upisana u bazu Arrvd")
   '   n.   Main.varTitle = Message.GetData.Get("title")

End Sub

Sub Service_Destroy

End Sub

Can someone help me to fix this, please

Thanks
 
Last edited:

ilan

Expert
Licensed User
Longtime User
Why do you save it in activity resume and not in your firebase service after u receive the notification!

Btw u set variables in main activity from a service that can be fired when main activity may be killed.

You should store that details to your db right away when u receive it. That makes more sense.
 
Upvote 0

Alvsky

Member
Licensed User
Longtime User
Ilan, thanks for quick reply.

I actually save data right away in FirebaseMessaging service (or at least I think so :D ) but there actually is my problem. Data does not get saved from some reason.

As you can see from the code, there should be "Message received" log entry after ** Service (firebasemessaging) Start ** but it is not happening after I start app from received message...
It just looks like fm_MessageArrived Sub is not called and I don't know why.
 
Last edited:
Upvote 0

ilan

Expert
Licensed User
Longtime User
No u dont save the data u just create a notification and update some variables in the main activity but this is wrong.

A service can run also when the main activity is killed that means u will not be able to access those variables from the service if the main activity is not in forground or background (=killed).

What u should do is u should move the db update code from activity resume to the service where u create the notification. So when u receive a notification successfully u update the db and create the notification THEN in activity resume or when ever u want u just READ from the db as ussual.
 
Upvote 0

Alvsky

Member
Licensed User
Longtime User
OK. Let's forget this Activity resume part. I used it as a workaround if update after receiving message is not working. Just ignore it. (I will delete it from post so it's not creating confusion anymore)
Second code part is a FirebaseMessaging Service where I try to save a message but with no success.
After a message arrives, I should get Log entry "Message arrived" which indicates that code has entered Sub fm_MessageArrived and do the DB INSERT.
This works if application runs in background or application is open.
But.. if app is closed, all I get is a notification in notification bar but no code in Sub fm_MessageArrived is run after I press notification. All it happens is that app is started and message dissapears.

I updated FirebaseMessaging Service code but it's the same
 
Last edited:
Upvote 0

ilan

Expert
Licensed User
Longtime User
OK. Let's forget this Activity resume part. I used it as a workaround if update after receiving message is not working. Just ignore it. (I will delete it from post so it's not creating confusion anymore)
Second code part is a FirebaseMessaging Service where I try to save a message but with no success.
After a message arrives, I should get Log entry "Message arrived" which indicates that code has entered Sub fm_MessageArrived and do the DB INSERT.
This works if application runs in background or application is open.
But.. if app is closed, all I get is a notification in notification bar but no code in Sub fm_MessageArrived is run after I press notification. All it happens is that app is started and message dissapears.

I updated FirebaseMessaging Service code but it's the same

you are confusing here some parts.

1. i prefer not to update codes in the thread so people will understand what was done wrong and what is the correct code so i would leave the code in the first post so people can do the comparison.

2. when you receive a notification the firebase service will raise even if your application is killed. that means the EVENT:

B4X:
Sub fm_MessageArrived (Message As RemoteMessage)

will be called and everything inside will be executed!!

now you decided to create a notification in that event (what make sense of course) and then if you press the notification the activity that is set in the notification will be opened. so clicking on the notification will NOT call:

B4X:
Sub fm_MessageArrived (Message As RemoteMessage)

it will just open the activity you have set (in your case the main activity)

so if you update your db in that event it will be updated and you should be able to read from it in your app

so your code should look like this:

B4X:
Sub fm_MessageArrived (Message As RemoteMessage)
   Log("Message arrived")
   Log($"Message data: ${Message.GetData}"$)

   Dim varTitle, varBody As String
   Dim dbS As SQL
   Dim ruta As String

   If File.ExternalWritable Then
     ruta = File.DirDefaultExternal
   Else
     ruta = File.DirInternal
   End If

   varTitle = Message.GetData.Get("title")
   varBody = Message.GetData.Get("body")

   dbS.Initialize(ruta, "FloorFitness.db", True)
   dbS.ExecNonQuery2("INSERT INTO messages VALUES (?, ?, ?, ?, ?)", Array As Object(Null, DateTime.Date(DateTime.Now),varTitle,varBody,"0"))

   Dim n As Notification
   n.Initialize
   n.Icon = "icon"
   n.SetInfo(varTitle, varBody, Main)'Otvara Main activity
   n.Notify(1)

   Log("Poruka upisana u bazu Arrvd")
   '   n.   Main.varTitle = Message.GetData.Get("title")

End Sub

although the problem i see in this code is what would happen if for some reason the If File.ExternalWritable Then will return false in the service but true in your app o_O

do you really need to do that check each time you write to the db?
why not do it once when the app starts for the first time and save a value that will tell you the directory of the db??

try to put the code above to your service and kiil your app from the background then connect your app via USB debug and send a notification to your app then check in the logs if the msg arrived and if the db was updated.you can put a log of checking the db size before you enter the values to your db and then another log of db.size after you have entered the log and you should see that your db has been updated.
 
Upvote 0

Alvsky

Member
Licensed User
Longtime User
I did exactly as you suggested but with same result. I can see notification with all data in Notification bar but when I click on it App opens with Main activity and notification is dismissed but no data is saved.

Regarding a route check I put it there so I make sure that is not the problem.

As for 1st post editing, I agree with you but did not think about it. I just wanted to remove confusion.

Here is also Starter service. Maybe it will help...

B4X:
#Region  Service Attributes
    #StartAtBoot: False
    #ExcludeFromLibrary: True

#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Service_Create
    'This is the program entry point.
    'This is a good place to load resources that are not specific to a single activity.
    CallSubDelayed(FirebaseMessaging, "SubscribeToTopics")
   
End Sub

Sub Service_Start (StartingIntent As Intent)

End Sub

Sub Service_TaskRemoved
    'This event will be raised when the user removes the app from the recent apps list.
End Sub

'Return true to allow the OS default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub Service_Destroy

End Sub
 
Upvote 0

ilan

Expert
Licensed User
Longtime User
so basically what you want to do is to show all notifications in a list when the user clicks a button.(save all notifications for later use)

could you make the test with a simple map instead of sql?

try this code and tell me if the DB updates:

(FirebaseService - make sure to update the topic to your topic!!)

B4X:
'FirebaseMessaging
#Region  Service Attributes
    #StartAtBoot: False
#End Region

#Region  Service Attributes
    #StartAtBoot: False
#End Region

Sub Process_Globals
   Type dbitem(var1 As Object, date As Long, title As String, body As String, var5 As Int)
   Private fm As FirebaseMessaging
   Public promolink As String
End Sub

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

Public Sub SubscribeToTopics
   fm.SubscribeToTopic("your topic here") '####### PUT YOUR TOPIC HERE ###########
End Sub

Sub Service_Start (StartingIntent As Intent)
   If fm.HandleIntent(StartingIntent) Then Return
End Sub

Sub fm_MessageArrived (Message As RemoteMessage)
    Log("Message arrived")
    Log($"Message data: ${Message.GetData}"$)

    Dim varTitle, varBody As String
    Dim myDB As Map
    myDB.Initialize
    If File.Exists(File.DirInternal,"FloorFitness.db") Then
        myDB = File.ReadMap(File.DirInternal,"FloorFitness.db")
    End If

    varTitle = Message.GetData.Get("title")
    varBody = Message.GetData.Get("body")

    Dim newdbitem As dbitem
    newdbitem.Initialize
    newdbitem.var1 = Null
    newdbitem.date = DateTime.Now
    newdbitem.title = varTitle
    newdbitem.body = varBody
    newdbitem.var5 = 0
    myDB.Put(newdbitem.date,newdbitem)

    Dim n As Notification
    n.Initialize
    n.Icon = "icon"
    n.SetInfo(varTitle, varBody, Main)'Otvara Main activity
    n.Notify(1)

    Log("Poruka upisana u bazu Arrvd")
End Sub

Sub Service_Destroy

End Sub

(activity resume in main activity)

B4X:
Sub Activity_Resume
    Dim myDB As Map
    myDB.Initialize

    If File.Exists(File.DirInternal,"FloorFitness.db") Then
        myDB = File.ReadMap(File.DirInternal,"FloorFitness.db")
    End If
   
    For Each key As Long In myDB.Keys
        Dim newdbitem As dbitem = myDB.Get(key)
        Log("Date:  " & DateTime.Date(newdbitem.date))
        Log("Title:  " & newdbitem.title)
        Log("Body:  " & newdbitem.body)
        Log("Int:  " & newdbitem.var5)
        Log("### new item ###")
    Next
End Sub

!!! BEFORE YOU DO THE TEST DELETE THE APP OR AT LEAST THE *.DB FILE SO THERE WONT BE A CONFLICT BECAUSE YOU SAVED IT BEFORE AS A SQL DB AND NOT AS A MAP!
 
Last edited:
Upvote 0

Alvsky

Member
Licensed User
Longtime User
you are righ about the app goal. I need messages to be saved so users can review them later in case they forget.

I tried your sollution. I uninstalled my app and reinstalled it with your code. This time I message doesn't get saved at all in DB. In any of the combinations.
 
Upvote 0

ilan

Expert
Licensed User
Longtime User
you are righ about the app goal. I need messages to be saved so users can review them later in case they forget.

I tried your sollution. I uninstalled my app and reinstalled it with your code. This time I message doesn't get saved at all in DB. In any of the combinations.

Yes it probably because maps cannot save type objects. U will need kvs for that anyway only for the test save instead of the custom type just a string as value in the map and see if it is saving

Just for tests. After that i will make a small app to show u how to use kvs with custom types. Its very powerful and easy to use.
 
Upvote 0

ilan

Expert
Licensed User
Longtime User
ok. I will need some time to learn how to use kvs :).

only for the tests do this:

Firebase Module (DONT FORGET TO UPDATE THE TOPIC TO YOUR TOPIC!!)

B4X:
'FirebaseMessaging
#Region  Service Attributes
    #StartAtBoot: False
#End Region

#Region  Service Attributes
    #StartAtBoot: False
#End Region

Sub Process_Globals
    Private fm As FirebaseMessaging
    Public promolink As String
End Sub

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

Public Sub SubscribeToTopics
    fm.SubscribeToTopic("your topic here") '####### PUT YOUR TOPIC HERE ###########
End Sub

Sub Service_Start (StartingIntent As Intent)
    If fm.HandleIntent(StartingIntent) Then Return
End Sub

Sub fm_MessageArrived (Message As RemoteMessage)
    Log("Message arrived")
    Log($"Message data: ${Message.GetData}"$)

    Dim varTitle, varBody As String
    Dim myDB As Map
    myDB.Initialize
    If File.Exists(File.DirInternal,"FloorFitness.db") Then
        myDB = File.ReadMap(File.DirInternal,"FloorFitness.db")
    End If

    varTitle = Message.GetData.Get("title")
    varBody = Message.GetData.Get("body")

    Dim newdbitem As String
    newdbitem = DateTime.Now & "|" & varTitle & "|" & varBody & "|" & 0
    myDB.Put(DateTime.Now,newdbitem)

    Dim n As Notification
    n.Initialize
    n.Icon = "icon"
    n.SetInfo(varTitle, varBody, Main)'Otvara Main activity
    n.Notify(1)

    Log("Poruka upisana u bazu Arrvd")
End Sub

Sub Service_Destroy

End Sub

Main Resume:

B4X:
Sub Activity_Resume
    Dim myDB As Map
    myDB.Initialize

    If File.Exists(File.DirInternal,"FloorFitness.db") Then
        myDB = File.ReadMap(File.DirInternal,"FloorFitness.db")
    End If
 
    For Each key As String In myDB.Keys
        Dim newdbitem() As String = Regex.Split("\|",myDB.Get(key))
        Log("Date:  " & DateTime.Date(newdbitem(0)))
        Log("Title:  " & newdbitem(1))
        Log("Body:  " & newdbitem(2))
        Log("Int:  " & newdbitem(3))
        Log("### new item ###")
    Next
End Sub

i have not tested this code but try it and let me know if it get saved.

after that i will make a small example with KVS its very simple and powerful. you can save any type of object to it and use custom types that are very powerful.
 
Upvote 0

Alvsky

Member
Licensed User
Longtime User
Thanks for effort. I'm quite busy these days with my regular job so I will try this as soon as I get some time.
I will let you know if it works
 
Upvote 0

Alvsky

Member
Licensed User
Longtime User
Finally I got some time to test again so here is what I did:

First, I had to save map file in fm_MessageArrived Sub in order to read from it in Main_Resume so it looks like this:
B4X:
Sub fm_MessageArrived (Message As RemoteMessage)
    Log("Message arrived")
    Log($"Message data: ${Message.GetData}"$)

    Dim varTitle, varBody As String
    Dim myDB As Map
    myDB.Initialize
    If File.Exists(File.DirInternal,"FloorFitnessMap.txt") Then
        myDB = File.ReadMap(File.DirInternal,"FloorFitnessMap.txt")
    End If

    varTitle = Message.GetData.Get("title")
    varBody = Message.GetData.Get("body")

    Dim newdbitem As String
    newdbitem = DateTime.Now & "|" & varTitle & "|" & varBody & "|" & 0
    myDB.Put(DateTime.Now,newdbitem)

    Log("FM_Map: "&myDB)

    File.WriteMap(File.DirInternal,"FloorFitnessMap.txt",myDB)
   
   
    Dim n As Notification
    n.Initialize
    n.Icon = "icon"
    n.SetInfo(varTitle, varBody, Main)'Otvara Main activity
    n.Notify(1)

    Log("Message written to DB Arrvd")
End Sub

I also created a new Map file (FloorFitnessMap.txt) so I make it simple.

Main Resume looks like this:
B4X:
Sub Activity_Resume
    Log ("--- Start Resume ---")
    Dim myDB As Map
    myDB.Initialize

    If File.Exists(File.DirInternal,"FloorFitnessMap.txt") Then
        myDB = File.ReadMap(File.DirInternal,"FloorFitnessMap.txt")
    End If
 
    Log("ResumeMap: " &myDB)
   
    For Each key As String In myDB.Keys
        Dim newdbitem() As String = Regex.Split("\|",myDB.Get(key))
        Log("Date:  " & DateTime.Date(newdbitem(0)))
        Log("Title:  " & newdbitem(1))
        Log("Body:  " & newdbitem(2))
        Log("Int:  " & newdbitem(3))
        Log("### new item ###")
    Next
Log ("--- End Resume ---")
End Sub

And here are results: In all combinations, message arrives, which is good.
But, actually same thing happens again: message gets saved only when app is open.
When app is closed, message arrives, but it is not saved.
Here are log files for combinations:

App closed OR in background:
- I get notification
- Clock on notification -> app opens
Log:
B4X:
*** Service (starter) Create ***
** Service (starter) Start **
** Activity (main) Create, isFirst = true **
** Activity (main) Resume **
--- Start Resume ---
ResumeMap: (MyMap) {}
--- End Resume ---
*** Service (firebasemessaging) Create ***
** Service (firebasemessaging) Start **
I pause app (I do not close) and resume it to doublecheck and still no notification is saved
B4X:
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
--- Start Resume ---
ResumeMap: (MyMap) {}
--- End Resume ---

Now app is open.
Notification arrives...
Log:
B4X:
** Service (firebasemessaging) Start **
Message arrived
Message data: {body=test3, title=PORUKA IZ FLOOR-a: test3}
FM_Map: (MyMap) {1509528685318=1509528685318|PORUKA IZ FLOOR-a: test3|test3|0}
Message written to DB Arrvd

Click on Notification
Log:
B4X:
** Activity (main) Pause, UserClosed = false **
** Activity (main) Resume **
--- Start Resume ---
ResumeMap: (MyMap) {1509528685318=1509528685318|PORUKA IZ FLOOR-a: test3|test3|0}
Date:  01.11.2017.
Title:  PORUKA IZ FLOOR-a: test3
Body:  test3
Int:  0
### new item ###
--- End Resume ---
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
You already have a working and running starter service.

Add the DB-code to the starter service.

From inside the FirebaseMessaging Service (when a message arrives) you just instruct the code in the starter-service to store the Message. No need to open an Activity to store them....

Then, later, when you run your app you can read all stored messages if you want.
 
Upvote 0

Alvsky

Member
Licensed User
Longtime User
Now I'm totally confused :)

My Starter service looks like this:
B4X:
#Region  Service Attributes
    #StartAtBoot: False
    #ExcludeFromLibrary: True

#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
End Sub

Sub Service_Create
    'This is the program entry point.
    'This is a good place to load resources that are not specific to a single activity.
    CallSubDelayed(FirebaseMessaging, "SubscribeToTopics")
End Sub

Sub Service_Start (StartingIntent As Intent)

End Sub

Sub Service_TaskRemoved
    'This event will be raised when the user removes the app from the recent apps list.
End Sub

'Return true to allow the OS default exceptions handler to handle the uncaught exception.
Sub Application_Error (Error As Exception, StackTrace As String) As Boolean
    Return True
End Sub

Sub Service_Destroy

End Sub

and FirebaseMessaging service like this:

B4X:
'FirebaseMessaging
#Region  Service Attributes
    #StartAtBoot: False
#End Region

#Region  Service Attributes
    #StartAtBoot: False
#End Region

Sub Process_Globals
    Type dbitem(var1 As Object, date As Long, title As String, body As String, var5 As Int)
    Private fm As FirebaseMessaging
    Public promolink As String
End Sub

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

Public Sub SubscribeToTopics
    fm.SubscribeToTopic("floor") '####### PUT YOUR TOPIC HERE ###########
End Sub

Sub Service_Start (StartingIntent As Intent)
    If fm.HandleIntent(StartingIntent) Then Return
End Sub

Sub fm_MessageArrived (Message As RemoteMessage)
    Log("Message arrived")
    Log($"Message data: ${Message.GetData}"$)

    Dim varTitle, varBody As String
    Dim myDB As Map
    myDB.Initialize
    If File.Exists(File.DirInternal,"FloorFitnessMap.txt") Then
        myDB = File.ReadMap(File.DirInternal,"FloorFitnessMap.txt")
    End If

    varTitle = Message.GetData.Get("title")
    varBody = Message.GetData.Get("body")

    Dim newdbitem As String
    newdbitem = DateTime.Now & "|" & varTitle & "|" & varBody & "|" & 0
    myDB.Put(DateTime.Now,newdbitem)

    Log("FM_Map: "&myDB)

    File.WriteMap(File.DirInternal,"FloorFitnessMap.txt",myDB)
   
    Dim n As Notification
    n.Initialize
    n.Icon = "icon"
    n.SetInfo(varTitle, varBody, Starter)'Otvara Main activity
    n.Notify(1)

    Log("Message written to DB Arrvd")
End Sub

Sub Service_Destroy

End Sub


How to can I pass message title and body from FM service to Starter service.

Sorry if I am asking some basic questions but this I just don't understand this...
 
Upvote 0
Top