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 tokenpart. - 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:
- https://learn.microsoft.com/en-us/xamarin/android/data-cloud/google-messaging/remote-notifications-with-fcm?tabs=windows
- 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.