I have implemented push notification on my Xamarin forms application using FCM.
My code
[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);
SendRegistrationTokenToMainPRoject(token);
}
public static string requestData = "";
public static string messageType = "";
public static string revealedLocationName = "";
public static string revealedLocationId = "";
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
var Body = message.GetNotification().Body;
var title = message.GetNotification().Title;
var id = message.MessageId;
string notifId = message.MessageId;
string b = string.Empty;
for (int i = 0; i < notifId.Length; i++)
{
if (Char.IsDigit(notifId[i]))
b += notifId[i];
}
requestData = message.Data["requestData"];
messageType = message.Data["commentMessageType"];
revealedLocationName = message.Data["revealedLocationName"];
revealedLocationId = message.Data["revealedLocationId"];
Console.WriteLine("requestData:>>" + requestData);
var myData = JsonConvert.DeserializeObject<RequestList>(requestData);
Console.WriteLine("myData:>>" + myData);
MessagingCenter.Send<object, string>(this, "requestData", requestData);
string requestId = myData.request.itemId;
MessagingCenter.Send<object, string>(this, "requestId_from_notification", requestId);
try
{
string body = System.Net.WebUtility.UrlDecode(message.GetNotification().Body.ToString());
string header = System.Net.WebUtility.UrlDecode(message.GetNotification().Title.ToString());
SendNotificatios(body, header);
}
catch (Exception ex)
{
Console.WriteLine("Error:>>" + ex);
}
}
public void SendNotificatios(string body, string Header)
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
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.ic_notification)
.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.ic_notification)
.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
{
//Save token Locally Here
MessagingCenter.Send<object, string>(this, "fcmtoken", token);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Here I am using notifications for the messages. So when I tap on any notification I read the data from notification and pass it to App.xaml
. From there I open my message page.
Notification Tapping:
//Background or killed mode
if (Intent.Extras != null)
{
foreach (var key in Intent.Extras.KeySet())
{
var value = Intent.Extras.GetString(key);
Log.Debug(TAG, "Key: {0} Value: {1}", key, value);
if (key == "requestData") // Make it Dynamic instead of hardcoding here
{
if (value?.Length > 0)
{
isNotification = true;
LoadApplication(new App(value, FirebaseNotificationService.messageType, FirebaseNotificationService.revealedLocationName, FirebaseNotificationService.revealedLocationId));
}
}
}
}
//Foreground mode
if (FirebaseNotificationService.requestData.ToString() != "")
{
isNotification = true;
LoadApplication(new App(FirebaseNotificationService.requestData.ToString(), FirebaseNotificationService.messageType, FirebaseNotificationService.revealedLocationName, FirebaseNotificationService.revealedLocationId));
FirebaseNotificationService.requestData = "";
}
//Normal loading
if (!isNotification)
{
LoadApplication(new App(string.Empty, FirebaseNotificationService.messageType,FirebaseNotificationService.revealedLocationName, FirebaseNotificationService.revealedLocationId));
}
My Notification data:
{
"to" : "device-fcm-token",
"notification" : {
"body" : "Hiii",
"title": "Title"
},
"data" : {
"commentMessageType": "customer",
"revealedLocationName": "Moncy Events Center",
"revealedLocationId": "77451",
"requestData":
{
"request": {
"itemId": 3226,
"itemTitle": "Need car maintanance service",
"itemDescription": "Need car maintanance service",
"itemType": "request",
"itemStatus": 1,
"itemSource": 0,
"itemPriority": 1
},
"category": "Vehicle Rental",
"companyId": 0,
"companyName": null
}
}
}
My problem is on the notification tapping on Foreground mode
, and a minor issue on background or killed mode
. On foreground mode, I am reading data in a wrong approach.
LoadApplication(new App(FirebaseNotificationService.requestData.ToString(), FirebaseNotificationService.messageType, FirebaseNotificationService.revealedLocationName, FirebaseNotificationService.revealedLocationId));
Here I have directly taking the requestData
, messageType
, revealedLocationName
, and revealedLocationId
variable values using the class FirebaseNotificationService.
So if one notification received, at that time all the above values will set on FirebaseNotificationService variables
. And if again a new notification received, the new notification values will set on above variables by replacing the first notification values. So the first notification values will lose and if I tap on it nothing will happen.
So in foreground mode, no matter how many notifications received, my logic will take the last received notifications values. Same mistake I have done on background mode for messageType
, revealedLocationName
, and revealedLocationId
variables.
I need to change this logic and read the exact value of tapped notification and pass it to App.xaml
. I have created a ticket on last year for the same issue, unfortunately I can't follow it properly. This is a show stopper issue for me, so please help me to fix this.