Android Question [Solved] FCM notifications

udg

Expert
Licensed User
Longtime User
Hi all,
I'm suffering a mental blackout about FCM notifications.
First of all, everything works. The reason for this post is that I'd like to show the simple notification only when the app is not in foreground.
So, when fm_MessageArrived is fired, I firstly operated on the data making up the message (i.e. update the local DB or execute a "command") then I'd like to choose whether to show a notification or not (eventually showing a toast or similar message window).
It should exist something telling me whether my B4xPages app is in its foreground state or it was awaken by the OS to process the FCM message. Simply I can recall it at this moment.

BTW, is NB6 adviced over simple notifications for basic messages (e.g. "it's your turn", "your order is ready",...)?

Thank you all.
 

udg

Expert
Licensed User
Longtime User
IsPaused(Main) partially works.
But we know that, when we close a B4xPages app, for some time it remains "active" in memory, so when FCM awakens it, IsPaused returns False even if our app is not the foreground one (making it unable to show a message on its pages).
Here a schematic way to show my program flow:
B4X:
'fm_MessageArrived
If Not(IsPaused(Main)) Then
            Dim m1 As Map
            m1.Initialize
            m1.Put("param1", MData.Get("p1"))    'actual values are more meaningful; this is just to let you follow the flow
            m1.Put("param2", MData.Get("p2"))
            CallSubDelayed2(Main, "FCMOMessage", m1)
 Else
   'send simple notification
end if

'Main
Sub FCMOMessage(m As Map)            'message from service FirebaseMessaging
    Log("**Main FCM OMessage**""")
    Log(B4XPages.GetManager.GetTopPage.Id)
    B4XPages.GetManager.RaiseEvent(B4XPages.GetManager.GetTopPage, "fcm_omessage", Array(m, 1))
End Sub

'Pages where showing a message is appropriate
Sub fcm_omessage(amap As Object, code As Int)
    Dim mmap As Map = amap
    Dim msg As String = $"BlahBlah${CRLF}Param1: ${mmap.Get("param1")}${CRLF}Param2: ${mmap.Get("param2")}${CRLF}"$
    Dim xui As XUI
    Dim sf As Object = xui.MsgboxAsync(msg, "WARNING")
    Wait For (sf) Msgbox_Result (Result As Int)
    If Result = xui.DialogResponse_Positive Then
        '
    End If

So the problem remains: how to discriminate wether the user is in the OS Home launching page or actively using another app (time when a notification is useful) or he/she is using my app (time to show an in-app message, if needed).
Hmmm, maybe a global var or a persisting value where I could save the app status (running or recently exited). Although something external to the app code would be more elegant.
Anyway, it's no critical. I opened the thread because I had the sensation that I was missing something obvious and simple (IsPaused is a good example).
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Update
I did a few tests. Here's what I found (not unexpected, but not able to solve the initial problem too).
IsPaused(Main) returns True when
- the device is turned on (an FCM was sent while the device was turned off; turn on the device, don't launch the app; app receives an FCM and show a notification)
- the device is rebooted (an FCM is sent while the device is turned on but the app is not yet launched; app receives an FCM and show a notification)
In this circumstances I can show a notification to the user (there's a collateral annoying quirk about initializing the DB, but I solved it anyway)
IsPaused(Main) returns False when
- app is launched clicking on the notification message
- app is in foreground while an FCM arrives
- app is in background (still alive in OS cache) due to exiting it by BackKey or Home key

The above seems to suggest that I need a way to know when the app is in background (in order to show a notification). Since I doubt that the OS will tell me whether my app is currently in its cache, I'm inclined to try to record when my app do a transition between foreground to background and viceversa. Then set a variable in Starter (which should be alive any time the FCM service fires) and use its valure to determine whether I need a notification or not.
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
The above seems to suggest that I need a way to know when the app is in background (in order to show a notification). Since I doubt that the OS will tell me whether my app is currently in its cache, I'm inclined to try to record when my app do a transition between foreground to background and viceversa. Then set a variable in Starter (which should be alive any time the FCM service fires) and use its valure to determine whether I need a notification or not.
I don't entirely agree.
If the method were to work in iOS as well, in this, as far as I know, there are no services.

You will probably have to manage locally saved data whose value you will need to set in the Background and Foreground events.
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Two considerations.
I've tried the fore/back solution (using Starter) and it works.
Second, I agree that iOS is another story (btw, there's no Starter service but I could use Application Start in its place). Setting a process var or saving a value locally doesn't change the overall scheme. The key point remains to track the fore/back state transition.
I'm sure that iOS will impose other special considerations; anyway B4xPages aim to reduce the code valid for just one platform.

So, at this moment, my working solution modifies post #3 as follows:
B4X:
'fm_MessageArrived
...
'using topics, not device token
If IsPaused(Main) Then 
   'init and open the DB <-- this is because I originally moved this part from Starter to a class
   ' check with DB if the message is appropriate for this device
   'eventually, update local data and send a notification
else
  ' check with DB if the message is appropriate for this device
   'eventually, update local data accordingly
   If Starter.isForeground Then
      'prepare internal message and deliver it through the delegation scheme
      Dim m1 As Map
      m1.Initialize
      m1.Put("param1", MData.Get("p1"))    'actual values are more meaningful; this is just to let you follow the flow
      m1.Put("param2", MData.Get("p2"))
      CallSubDelayed2(Main, "FCMOMessage", m1)
  else
     send a notification
  end if
end if
 
Last edited:
Upvote 0

udg

Expert
Licensed User
Longtime User
In this case the process var (Starter.isForeground) will be wiped out when the process will be killed by the OS. When that occurs we are back to IsPaused(Main)= True which leads to a notification (as it should). Launching again (from the notification or the launcher) will re-create the Starter, IsPaused will go to False and the fore/back mechanism will start again.
As said, what I now do with Starter I could do with data saved locally. At the moment I'm more interested to verify whether the above works in all cirmstances or not.
And eventually to hear if there'a a better way to achieve my original goal.
This is why I marked the thread as solved.
 
Last edited:
Upvote 0

Andrew (Digitwell)

Well-Known Member
Licensed User
Longtime User
I know that you have marked this solved but...

b4xpages has 2 events:

B4xPage_Background
B4xPage_Foreground

Which are invoked when the app transitions between these states. This is cross platform. I declare these functions in B4xMainPage.

My use case is to lock the app - requiring a password to unlock - if the app moves from the foreground or is inactive for a period of time.

This works fine.
 
Upvote 0

udg

Expert
Licensed User
Longtime User
Thank you Andrew, that was exactly what I used.
I set Starter.isForeground to true/false in those events (MainPage) so to discriminate when to send an internal message or raise an external notification.
This is valid when the app was launched at least once or it is in the OS cache (exited but process still alive).
Following a boot, re-boot or app purged from the OS cache ( and not yet manually re-launched), the IsPaused(Main) code in FirebaseMessaging service assures the raising of the external notification.
 
Upvote 0
Top