0

Below is my MainActivity code:

using System;
using Android.App;
using Android.Content.PM;
using Android.Runtime;
using Android.OS;
using static Firebase.Messaging.RemoteMessage;
using Android.Gms.Common;
using Android.Nfc;
using Android.Util;
using Android.Content;
using Firebase.Messaging;
using NoticationDemo.Droid;
using System.Collections.Generic;
using Xamarin.Forms;
using AndroidX.Core.App;

namespace NoticationDemo.Droid
{
    [Activity(
        Label = "NoticationDemo", 
        Icon = "@mipmap/icon", 
        Theme = "@style/MainTheme", 
        MainLauncher = true,
        LaunchMode = LaunchMode.SingleTop,
        ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation | ConfigChanges.UiMode | ConfigChanges.ScreenLayout | ConfigChanges.SmallestScreenSize )]
    public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
    {
        static readonly string TAG = "MainActivity";
        public bool isNotification = false;
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            Xamarin.Essentials.Platform.Init(this, savedInstanceState);
            global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
            isNotification = false;
            IsPlayServicesAvailable();
            //background mode or killed mode
            CreateNotificationFromIntent(Intent);

            if (!isNotification)
            {
                LoadApplication(new App("No Notification"));
            }
        }

        public bool IsPlayServicesAvailable()
        {
            int resultcode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
            if (resultcode != ConnectionResult.Success)
            {
                if (GoogleApiAvailability.Instance.IsUserResolvableError(resultcode))
                {
                    Console.WriteLine($"Error:{GoogleApiAvailability.Instance.GetErrorString(resultcode)}");
                }
                else
                {
                    Console.WriteLine("Error: play services not supported!");
                }
                return false;
            }
            else
            {
                Console.WriteLine("Play services available");
                return true;
            }
        }

        protected override void OnNewIntent(Intent intent)
        {
            //foreground mode
            CreateNotificationFromIntent(intent);
        }

        void CreateNotificationFromIntent(Intent intent)
        {
            if (intent.Extras != null)
            {
                foreach (var key in intent.Extras.KeySet())
                {
                    var notificationName = intent.Extras.GetString("NotificationName");
                    var NotificatioKey = intent.Extras.GetString("NotificationKey");

                    Console.WriteLine("NotificationName:>>" + notificationName);
                    Console.WriteLine("NotificatioKey:>>" + NotificatioKey);

                    if (NotificatioKey == "Superman")
                    {
                        if (notificationName?.Length > 0)
                        {
                            isNotification = true;
                            LoadApplication(new App(notificationName));
                        }
                    }
                }
            }
        }

        public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
        {
            Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);

            base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
        }
    }
}

[Service(Enabled = true, Exported = true)]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseNotificationService : FirebaseMessagingService
{
    public override void OnNewToken(string token)
    {
        base.OnNewToken(token);
        Console.WriteLine($"Token received:>> {token}");
        SendRegistrationTokenToMainPRoject(token);
    }

    public override void OnMessageReceived(RemoteMessage message)
    {
        base.OnMessageReceived(message);
        try
        {
            string body = System.Net.WebUtility.UrlDecode(message.GetNotification().Body.ToString()).Replace("'", "'");
            string header = System.Net.WebUtility.UrlDecode(message.GetNotification().Title.ToString()).Replace("'", "'");
            SendNotificatios(body, header, message.Data);
        }
        catch (Exception ex)
        {
            Console.WriteLine("Error:>>" + ex);
        }
    }

    public void SendNotificatios(string body, string Header, IDictionary<string, string> data)
    {
        var intent = new Intent(this, typeof(MainActivity));
        intent.AddFlags(ActivityFlags.ClearTop);
        foreach (var key in data.Keys)
        {
            intent.PutExtra(key, data[key]);
        }
        var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.Mutable);

        if (Build.VERSION.SdkInt < BuildVersionCodes.O)
        {
            var notificationBuilder = new NotificationCompat.Builder(this)
                        .SetContentTitle(Header)
                        .SetContentText(body)
                        .SetSmallIcon(Resource.Mipmap.icon)
                        .SetAutoCancel(true)
                        .SetContentIntent(pendingIntent);

            var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;

            notificationManager.Notify(new Random().Next(), notificationBuilder.Build());
        }
        else
        {
            var notificationBuilder = new NotificationCompat.Builder(this, Utils.CHANNEL_ID)
                        .SetContentTitle(Header)
                        .SetContentText(body)
                        .SetSmallIcon(Resource.Mipmap.icon)
                        .SetAutoCancel(true)
                        .SetContentIntent(pendingIntent);

            var notificationManager = GetSystemService(Context.NotificationService) as NotificationManager;

            NotificationChannel channel = new NotificationChannel(Utils.CHANNEL_ID, "FCM Notifications", NotificationImportance.Default);
            notificationManager.CreateNotificationChannel(channel);

            notificationManager.Notify(new Random().Next(), notificationBuilder.Build());
        }
    }

    void SendRegistrationTokenToMainPRoject(string token)
    {
        try
        {
            //Send Refresh to you FCM Server Here
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message);
        }
    }
}

AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.noticationdemo">
    <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
    <application android:label="NoticationDemo.Android" android:theme="@style/MainTheme">
      <receiver android:name="com.google.firebase.iid.FirebaseInstanceIdInternalReceiver" 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>
    </application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
</manifest>

Utils.cs

public class Utils
{
    public static readonly string CHANNEL_ID = "CB_FCM_CHANNEL";
    public static readonly int NOTIFICATION_ID = 100;
}

Notification Payload:

{
"to" : "device token",
"notification" : {
    "body" : "Hi1",
    "title": "Notification1"
},
"data" : {
    "NotificationName": "notification1",
     "NotificationKey" : "test"
    }
}

I am reading the NotificationName from the notification and passing it as an argument to App.xaml.cs and showing it on the UI of Mainpage.

public App(string notificationName)
{
    InitializeComponent();
    MainPage = new MainPage(notificationName);
}

public partial class MainPage : ContentPage
{
    public MainPage(string notificationName)
    {
        InitializeComponent();
        notificationname_label.Text = notificationName; 
    }
}

My issue is the first notification in foreground mode is always showing for all the notifications receiving in foreground mode.

For eg: I received Notification1 in foreground mode. If I tap on it the notification 1 data will read and it will show on the UI. Again if I receive a new notification, Notification2, if I tap on that, the notification 1 data is again showing on the UI. Always reading the very first notification data in foreground mode. No such issue in background mode.

Here is my demo and postman collection to recreate this issue.

How to recreate this issue:

  • Run the demo project into an android device and fetch your device FCM token. Add it on the postman collection APIs, on your device token part.
  • Then send API named Notification 1 in foreground mode. When tap on it then notification1 text will show on the UI.
  • Then if you send Notification 2 in foreground mode and tap on it again notification1 text will show on the UI instead of notification2.
  • The initial notification data is showing for all the notifications received in foreground mode. But no such issue in background mode and everything is working fine on background mode.

Referred blogs:

  1. https://learn.microsoft.com/en-us/xamarin/android/data-cloud/google-messaging/remote-notifications-with-fcm?tabs=windows
  2. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/app-fundamentals/local-notifications#handle-incoming-notifications-on-android

Update

I have added the MessagingCenter.Send inside of CreateNotificationFromIntent like below:

void CreateNotificationFromIntent(Intent intent)
{
    if (intent.Extras != null)
    {
        foreach (var key in intent.Extras.KeySet())
        {
            var notificationName = intent.Extras.GetString("NotificationName");
            var NotificationKey = intent.Extras.GetString("NotificationKey");

            Console.WriteLine("NotificationName:>>" + notificationName);
            Console.WriteLine("NotificationKey:>>" + NotificationKey);

            if (NotificationKey == "test")
            {
                if (notificationName?.Length > 0)
                {
                    isNotification = true;
                    MessagingCenter.Send<Xamarin.Forms.Application, string>(Xamarin.Forms.Application.Current, "NotificationName", notificationName);
                    LoadApplication(new App(notificationName));
                }
            }
        }
    }
}

Updated MainPage contents with MessagingCenter.Subscribe part.

public partial class MainPage : ContentPage
{
    public MainPage(string notificationName)
    {
        InitializeComponent();
        //notificationname_label.Text = notificationName;
        MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(this, "NotificationName", async (sender, arg) =>
        {
            notificationname_label.Text = arg;
        });
    }
}

But when tap on notification the notification name is not showing on the UI. When I debug it by adding breakpoints, the MessagingCenter.Send and MessagingCenter.Subscribe are invoking infinte times, but the notification name is not visible on mainpage UI.

1 Answer 1

0

My issue is the first notification in foreground mode is always showing for all the notifications receiving in foreground mode.

This is because you set the UI label in the MainPage's constructor by notificationname_label.Text = notificationName;. When the app run in foreground, the App.cs and MainPage's constructor will only be called when the MainPage initializes first time.

So you can refer to official document about using INotificationManager helper class to receive the notification.

In addition, you can also using the MessagingCenter to do that. Here is a case about using MessagingCenter to notify UI that the notifiction is updated.

Update:

If you use the MessagingCenter, you can send the new notification name when you receive it. And subscribe it in the MainPage's constructor:

Send the new notification name:

MessagingCenter.Send<Xamarin.Forms.Application, string>(Xamarin.Forms.Application.Current, "NotificationName","the new notification name you want to send")

Receive the new notification name:

        public MainPage()
        {
            InitializeComponent();

            // Subscribe to a message (which the ViewModel has also subscribed to) to display an alert
            MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(Xamarin.Forms.Application.Current, "NotificationName", async (sender, arg) =>
            {
                notificationname_label.Text = arg; 
            });
        }

For more information, you can refer to using MessagingCenter to notify UI that the notification is updated and the official sample about the MessagingCenter.

Sign up to request clarification or add additional context in comments.

27 Comments

Can you explain little more? I didn't get completely. On where I need to use the MessagingCenter and INotificationManager? Is it possible to update the demo with this fix and share?
@SreejithSree You can check the update part in my answer. And you can read the official document about the MessagingCenter at first.
Why you are using diferent keys on MessagingCenter.Send and MessagingCenter.Subscribe, Is it by mistake? And why the value is not sending on MessagingCenter.Send ? Without sending how we will get it on MainPage?
Sorry for my mistake. It should be MessagingCenter.Subscribe<Xamarin.Forms.Application, string>(this, "NotificationName", async (sender, arg) => @SreejithSree
Still i am confused on send part. Why the value is not sending on MessagingCenter.Send? Without sending value how we will get it on MainPage?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.