FCM notification not received when app is terminated

Adrian Jakubcik 111 Reputation points
2022-01-06T19:10:01.91+00:00

I'd like to ask a question regarding Firebase Cloud Messaging. So recently, I've implemented FCM notifications into my app, created the API to send notifications, etc. But I came to a halt regarding the notifications when the app is closed or terminated ... simply not running in the background. If I send a notification to my app while it is in the foreground or background it shows up as it should and gets processed however, if I terminate the app, stop it, or just remove it from the background and send a notification it is never received, it never shows up, etc. until I start the app again and wait around 5 - 10mins only then it arrives. I do want to point out that I'm testing it in a debug mode so if that is the problem I will be very pleased to hear that. In advance, I would like to say that yes I am sending the notification to a certain topic, and yes I am subscribed to that topic, and the notification that I am sending is a payload.

Also, one side question, when creating a local notification and showing it is there a way to customize the way it is shown? Because for some bizarre reason it shows the Icon and App name in the Taskbar and in the notification box only the Title, Message, and LargeIcon is shown. As so
162939-screenshot-1641494709.png

If possible I'd like to have it all in the notification box because else it can be a little harder to understand where the notification is from.
Also, I tried but failed to create a local notification with vibration and sound. I tried searching and doing what ppl have suggested but I just can't make the phone vibrate and it only makes a sound even when I set the defaults to sound only. Yeah I know emulators won't vibrate so I tested it on a real physical device, no vibration just the default android sound.

Xamarin
Xamarin
A Microsoft open-source app platform for building Android and iOS apps with .NET and C#.
5,294 questions
{count} votes

Accepted answer
  1. JarvanZhang 23,936 Reputation points
    2022-01-17T01:39:49.163+00:00

    handling it there must be performed by doing a navigation command after LoadApplication()

    Do you want to display a different main page for the user? Try to perform the navigation in the shared project instead.

    Pass a parameter when calling LoadApplication method.

    protected override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);
        Xamarin.Essentials.Platform.Init(this, savedInstanceState);
        global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
        LoadApplication(new App(parameter));
    }
    

    Then detect the value to show a different page.

    public partial class App : Application
    {
        public App(bool parameter)
        {
            InitializeComponent();
    
            if (parameter)
            {
                MainPage = new NavigationPage(new MainPage());
            }
            else 
            {
                MainPage = new NavigationPage(new TestPage());
            }
        }
    }
    

4 additional answers

Sort by: Most helpful
  1. JarvanZhang 23,936 Reputation points
    2022-01-13T06:52:50.647+00:00

    This is very strange because the navigation sets as root the first page ...

    This is because the application is reloaded when clicking the notifiation. To avoid this, try setting the LaunchMode of MainActivity to SingleTop, and add ActivityFlags.ClearTop flag to the intent.

    Here is the sample code, you could refer to it.

    [Activity(Label = "TestApplication1", MainLauncher = true, LaunchMode = LaunchMode.SingleTop, ...)]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);
    
            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            LoadApplication(new App());
        }
    
        protected override void OnNewIntent(Intent intent)
        {
            base.OnNewIntent(intent);
    
            if (intent.HasExtra("the_key") && intent.GetStringExtra("the_key") == "the_value")
            {
                App.Current.MainPage.Navigation.PushAsync(new TestPage());
            }
        }
    }
    ...
    
    Intent intent = new Intent(context, typeof(MainActivity));
    intent.AddFlags(ActivityFlags.ClearTop);
    intent.PutExtra("the_key", "the_value");
    

  2. Adrian Jakubcik 111 Reputation points
    2022-01-13T09:51:32.99+00:00

    @JarvanZhang I have created a Github repo with a sample app to show you what I mean, the steps to reproduce this problem are in the README
    Github repo: XamarinForms-NavigationBug


  3. Adrian Jakubcik 111 Reputation points
    2022-01-17T17:24:37.027+00:00

    Long story short anyone who's experiencing the same problem as the title of this post says the original problem that was discussed here. The solution for me was that after closing the app the debug session in Visual Studio is terminated thus also the google service which is responsible for receiving the notifications. Now to start it up again all I had to do is go to my emulator or physical device and start up the app manually not through Visual Studio, after it's fully loaded you can close it down or remove it from the background processes, and now your notifications should be coming once again. This may, however, be individual for every developer since nearly every developer has his own ways of implementing push notifications with Firebase Cloud Messaging.

    The vibration and sound problem was solved by creating a channel and setting the vibration to true for that channel, however, don't set the pattern of the vibration there. Enable lights if you'd like to set light color for your notifications, and set the sound that you'd want for your notifications. Like so

    void CreateNotificationChannel()
            {
                //No need for Notification Channel creation on devices under the Oreo version API 26
                if (Build.VERSION.SdkInt < BuildVersionCodes.O) return;
    
                var channel = new NotificationChannel(CHANNEL_ID, "Firebase Cloud Messaging Notifications", NotificationImportance.Max)
                {
                    Description = "Firebase Cloud Messages are handled by this channel.",
                    LightColor = Android.Graphics.Color.OrangeRed,
                    LockscreenVisibility = NotificationVisibility.Public,
                };
                channel.EnableLights(true);
                channel.EnableVibration(true);
                channel.SetSound(
                RingtoneManager.GetDefaultUri(RingtoneType.Notification),
                new AudioAttributes.Builder().SetUsage(AudioUsageKind.Notification).Build());
    
                manager = (NotificationManager)GetSystemService(NotificationService);
                manager.CreateNotificationChannel(channel);
            }
    

    Then when building the local notification you have to set the vibration pattern, set the defaults, and set the priority. Like so

    NotificationCompat.Builder builder = new NotificationCompat.Builder(this, MainActivity.CHANNEL_ID)
        .SetContentIntent(pendingIntent)
        .SetContentTitle(Notification.Title)
        .SetContentText(Notification.Description)
        .SetSmallIcon(Resource.Drawable.icon)
        .SetAutoCancel(true)
        .SetVibrate(new long[] { 300, 0, 300 })
        .SetPriority((int)NotificationPriority.Max)
        .SetDefaults((int)NotificationDefaults.All)
    

    And that's pretty much it for this problem.

    1 person found this answer helpful.
    0 comments No comments

  4. Adrian Jakubcik 111 Reputation points
    2022-01-12T19:27:10.05+00:00

    @JarvanZhang So after few tests and debugging I found out that the way Navigation handles navigation from MainActivity is very interesting... If my MainActivity looks like this

    protected override void OnCreate(Bundle savedInstanceState)  
            {  
                base.OnCreate(savedInstanceState);  
      
                Xamarin.Essentials.Platform.Init(this, savedInstanceState);  
                global::Xamarin.Forms.Forms.Init(this, savedInstanceState);  
                LoadApplication(new App());  
                if(Intent.Extras != null)  
                    ProcessIntent(Intent);  
      
      
                IsGooglePlayServiceAvailable = IsPlayServicesAvailable();  
                if (IsGooglePlayServiceAvailable)  
                {  
                    CreateNotificationChannel();  
                }  
            }  
      
            protected override void OnNewIntent(Intent intent)  
            {  
                base.OnNewIntent(intent);  
                ProcessIntent(intent);  
            }  
      
            protected void ProcessIntent(Intent intent, App app = null)  
            {  
                var type = (IntentTypes)intent.GetIntExtra("type", 1);  
                if (type == IntentTypes.Notification)  
                {  
                    var action = (NotificationActionTypes)intent.GetIntExtra("action", (int)NotificationActionTypes.OpenNotificationCenter);  
                    if (app == null) NotificationAction(action);  
                    else NotificationAction(action, app);  
                }  
            }  
      
            protected async void NotificationAction(NotificationActionTypes action, bool PopToRoot = true, Intent intent = null)  
            {  
                switch (action)  
                {  
                    case NotificationActionTypes.OpenNotificationCenter:  
                        //if(PopToRoot) await App.Current.MainPage.Navigation.PopToRootAsync();  
                        App.Current.MainPage.Navigation.PushAsync(new Views.NotificationsListPage());  
                        await App.Current.MainPage.Navigation.PopToRootAsync();  
                        await App.Current.MainPage.DisplayAlert("Count", $"Navigation Stack: {App.Current.MainPage.Navigation.NavigationStack.Count}", "OK");  
                        break;  
                    case NotificationActionTypes.OpenDirectByType:  
                        break;  
                }  
            }  
    

    For debugging purpose after loading the specific View I added code to PopToRoot just to see what happens, because the Alert displays there are 2 pages in Navigation Stack, and the one I land on is the NotificationListPage so theoretically poping to root should take me to the App.Current.MainPage however, instead the Application crashes or backs out. If the await App.Current.MainPage.Navigation.PopToRootAsync(); Was to be removed you'd land on NotificationListPage... but you won't be abe to use the back button nor the device back button it doesn't do anything. So, I don't know whether this is a bug in the Navigation itself since I am pushing to navigation stack of the MainPage but it looks like it overwrites it yet there are 2 pages in the stack but I can't navigate backwards.