Currently, I have push notifications being sent and received in my Xamarin app. When I send a push notification the sneak peak dialogue is displayed when the app is in the foreground & also the background. I am trying to only visual display a push notification when the app is in the background. When the user is in the foreground the notification just sneaks into the HUD and is not displayed.
Here is my code:
Xamarin Android
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FCMMessagingService : FirebaseMessagingService
{
const string TAG = "MyFirebaseMsgService";
public override void OnMessageReceived(RemoteMessage message)
{
try
{
var _message = message.GetNotification().Body;
var _title = message.GetNotification().Title;
MainActivity.SendNotification(_title, _message);
}
catch (Exception e)
{
//Was NotificationType null? this should never be null anyways
}
}
}
API - Android Payload
return new JObject(
new JObject(
new JProperty("notification",
new JObject(
new JProperty("body", message),
new JProperty("title", title),
new JProperty("icon","toasticon"),
new JProperty("user", fromId != null ? fromId.ToString() : "")
)
),
new JProperty("data",
new JObject(
new JProperty("notificationtype", notificationType)
)
)
)
).ToString();
If you have followed the firebase setup for xamarin android
You might have read there that foreground notifications have to be displayed manually,
How to do that?
This is how
In the setup, you will have a class inheriting from FirebaseMessagingService
In that class, there will be a piece of code something like this():
var pendingIntent = PendingIntent.GetActivity(this,
MainActivity.NOTIFICATION_ID,
intent,
PendingIntentFlags.OneShot);
var notificationBuilder = new NotificationCompat.Builder(this, MainActivity.CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)
.SetContentTitle("FCM Message")
.SetContentText(messageBody)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
var notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(MainActivity.NOTIFICATION_ID,
notificationBuilder.Build());
This piece of code sends the notification to the Android tray, When in the foreground and hence if you comment this you will get the desired output.
Good luck!
In case of queries revert.
Related
I am working on integrating firebase push notification to my application.
Please find my firebase FirebaseMessagingService class.
If the app is open and running everything is working fine. But if the app is not open / if I switch to some other app (my app is not closed). I am getting notification but when I tap on Notification it relaunches the app without resuming.
I am using launch mode LaunchMode = LaunchMode.SingleTop in my main activity.
And if the app is open I am getting the response in OnNewIntent override method of main activity.
Can anyone please help me to figure of the real cause. Please help.
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class DriverAppMessagingService : FirebaseMessagingService
{
#region Overriden Methods
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
var parameters = new Dictionary<string, object>();
var notification = message.GetNotification();
if (null != notification)
{
if (!string.IsNullOrEmpty(notification.Body))
{
parameters.Add("Body", notification.Body);
}
if (!string.IsNullOrEmpty(notification.BodyLocalizationKey))
{
parameters.Add("BodyLocalizationKey", notification.BodyLocalizationKey);
}
// convert the incoming message to a local notification
SendLocalNotification(parameters);
// send the incoming message directly to the MainActivty
SendNotificationToMainActivity(parameters);
}
}
public override void OnNewToken(string p0)
{
base.OnNewToken(p0);
//Persist the token to app settings for registration purpose.
AppDefinition.Helpers.Settings.Current.PnsHandle = p0;
}
#endregion
#region Private Methods
/// <summary>
///
/// </summary>
/// <param name="args"></param>
private void SendNotificationToMainActivity(Dictionary<string, object> args)
{
if (CrossCurrentActivity.Current.Activity is MainActivity activity)
{
var message = args["Body"].ToString();
activity.TriggerPushNotification(message);
}
}
/// <summary>
/// Method to trigger the local notification.
/// </summary>
/// <param name="args"></param>
private void SendLocalNotification(Dictionary<string, object> args)
{
//TODO Only using one token from message response.
var message = args["Body"].ToString();
var intent = new Intent(CrossCurrentActivity.Current.Activity, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
intent.PutExtra("message", message);
var pendingIntent = PendingIntent.GetActivity(CrossCurrentActivity.Current.Activity, 0, intent, PendingIntentFlags.UpdateCurrent | PendingIntentFlags.OneShot);
var notificationBuilder = new NotificationCompat.Builder(CrossCurrentActivity.Current.Activity, Constants.NotificationChannelName)
.SetContentTitle(Constants.ContentTitle)
.SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)
.SetContentText(message)
.SetAutoCancel(true)
.SetShowWhen(false)
.SetLights(0xff0000, 100, 100)
.SetContentIntent(pendingIntent);
if (Build.VERSION.SdkInt >= BuildVersionCodes.O)
{
notificationBuilder.SetChannelId(Constants.NotificationChannelName);
}
var notificationManager = NotificationManager.FromContext(CrossCurrentActivity.Current.Activity);
notificationManager.Notify(0, notificationBuilder.Build());
}
#endregion
}
As you set the MainActivity LaunchMode = LaunchMode.SingleTop and set ActivityFlags.ClearTop,when you tap the notification to open your application,it will clear all activities above MainActivity and place MainActivity at the top of the stack.
Instead of recreating MainActivity, it enters the OnNewIntent method.
You could make a breakpoint in OnCreate method,when you open the application after tap the notification,it will not step into it.
Data messages - Handled by the client app. These messages trigger the onMessageReceived() callback even if your app is in foreground/background/killed. When using this type of message you are the one providing the UI and handling when push notification is received on an Android device.
{
"data": {
"message" : "my_custom_value",
"other_key" : true,
"body":"test"
},
"priority": "high",
"condition": "'general' in topics"
}
Try this, this will solve your issue.
I am writing an Xamarin.forms based app which is currently running on android platform. It is the first time I need to use push-notifications. I followed a guide from microsoft (https://learn.microsoft.com/de-de/xamarin/android/data-cloud/google-messaging/remote-notifications-with-fcm?tabs=vswin)") to implement the notifications.
The target android version is 8.1 API 27. The app runs on a Samsung tab active 2, which has android 8.1.
I configured the app as seen in the tutorial. I push the messages through a defined channel and this channel is subscribed in the app. The messages are pushed by a server which triggers the rest call for the FCM api. The first day I did some tests the transmission worked very good and I would say it was (nearly) reliable.
The next day I implemented some other features and wanted to test the push notifications again. Then: I was very confused, the most messages were not delivered or VERY VERY late. I am not sure if all messages were transmitted, there went may be some lost.
For me the FCM service is a big blackbox where I can delegate some work and then I need to hope that the messages will be transmitted. I am very confused now.
I paste here some code, but it is nearly what you can find in the tutorial:
My Questions:
What can I do? Is there something to get some more information from the FCM what my messages are currently doing? Or are there some problems with the code?
This is run in the mainActivity:
if (this.IsPlayServicesAvailable())
{
// Creates a notification channel
this.CreateNotificationChannel();
//Console.WriteLine("InstanceID token: " + FirebaseInstanceId.Instance.Token);
// Subscribe to notification token
FirebaseMessaging.Instance.SubscribeToTopic("channel");
Log.Debug(TAG, "Subscribed to remote notifications");
}
This checks if the channel can be created and creates it: (is called in the mainActivity)
private void CreateNotificationChannel()
{
// The app is not running on Android 8.0 or higher
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
// Create a notification channel for publishing notifications
var channel = new NotificationChannel(CHANNEL_ID, "FCM Notifications", NotificationImportance.Default)
{
Description = "Firebase Cloud Messages appear in this channel"
};
var notificationManager = (NotificationManager)GetSystemService(Android.Content.Context.NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
This checks if playServices are available: (also called in mainActivity)
public bool IsPlayServicesAvailable()
{
int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.Success)
{
if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
{
Log.Debug(TAG, GoogleApiAvailability.Instance.GetErrorString(resultCode));
}
else
{
Log.Debug(TAG, "This device has no compatible Google Play services APK - Download an APK from the Google Play Store or to enable it in the device's system settings!");
Finish();
}
return false;
}
else
{
Log.Debug(TAG, "Google Play Services are available.");
return true;
}
}
The last snipped is the service to handle a notification and inform the user:
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class CustomFirebaseMessagingService : FirebaseMessagingService
{
// Logging Tag
private static readonly string TAG = "CustomFirebaseMessagingService";
/* Handles data messages and notifications messages if the app is in foreground.
*
* Apps only have 10 seconds in which to handle an incoming Firebase Cloud Message.
* Any work that takes longer than this should be scheduled for background execution using a library such as the 'Android Job Scheduler' or the 'Firebase Job Dispatcher'.
*
*/
public override void OnMessageReceived(RemoteMessage message)
{
Log.Debug(TAG, "Message from: " + message.From);
// If the message data payload is not empty, display a notification
if (message.Data.Count > 0)
{
Log.Debug(TAG, "Data Payload: " + message.Data.ToString());
this.SendNotification(message.Data);
}
}
// Converts the incoming FCM message into a local notification
private void SendNotification(IDictionary<string, string> data)
{
Console.WriteLine("Push Message received");
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
if (data.TryGetValue("message", out string message))
{
foreach (var key in data.Keys)
{
intent.PutExtra(key, data[key]);
}
var pendingIntent = PendingIntent.GetActivity(this, MainActivity.NOTIFICATION_ID, intent, PendingIntentFlags.OneShot);
var notificationBuilder = new NotificationCompat.Builder(this, MainActivity.CHANNEL_ID)
.SetSmallIcon(Resource.Drawable.NotificationIcon)
.SetContentTitle("TITEL")
.SetContentText(message)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
var notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(MainActivity.NOTIFICATION_ID, notificationBuilder.Build());
}
}
}
I used these instructions to add Azure Notification Hub FCM-based Remote Notifications to my Xamarin.Forms Android app.
https://learn.microsoft.com/en-us/azure/app-service-mobile/app-service-mobile-xamarin-forms-get-started-push
I can receive Remote Notifications when the app is open or running in the background, but when I close the application or stop the application I no longer receive Remote Notifications.
My test device is running API level 22, so I'm using the following method to to build the notification on the device.
Android.Support.V4.App.NotificationCompat.Builder builder = new
Android.Support.V4.App.NotificationCompat.Builder(this)
For API level 26+, I'm using the follow method along with a channel to build the notification on the device.
var builder = new Android.App.Notification.Builder(this)
I'm thinking that I have to use a BroadcastReceiver to achieve this, but I really have no idea after reading so many comments on this subject. My app is compiled using API 27 and targets API 27.
Method 1
I'm trying to create a BroadcastReceiver that will launch MyService using an Explicit Intent when a Notification arrives. Unfortunately, this does not work on my API Level 22 test device.
//[BroadcastReceiver]
//[IntentFilter(new[] { Android.Content.Intent.ActionBootCompleted })]
[BroadcastReceiver(Enabled = true, Exported = true)]
[IntentFilter(new[] { "com.xamarin.example.TEST" })]
public class MyBroadcastReceiver : BroadcastReceiver
{
public override void OnReceive(Context context, Intent intent)
{
string message = intent.GetStringExtra("message");
string title = intent.GetStringExtra("title");
string id = intent.GetStringExtra("id");
//Explicit Intent to launch MyService
Intent intent2 = new Intent(Application.Context, typeof(MyService));
intent2.PutExtra("message", message);
intent2.PutExtra("title", title);
intent2.PutExtra("id", id);
Application.Context.StartService(intent2);
}
}
// Service is exported and given a name so other applications can use it
[Service(Exported = true, Name = "com.mycompany.myapp.MyService")]
// Intent filter only needed for Implicit Intents
//[IntentFilter(new string[] { "com.xamarin.example.TEST" })]
public class MyService : Service
{
public static string PRIMARY_NOTIF_CHANNEL = "default";
public override IBinder OnBind(Intent intent)
{
return null;
}
[return: GeneratedEnum]
public override StartCommandResult OnStartCommand(Intent intent, [GeneratedEnum] StartCommandFlags flags, int startId)
{
var pm = PowerManager.FromContext(this);
var wakeLock = pm.NewWakeLock(WakeLockFlags.Partial, "Notification");
wakeLock.Acquire();
string message = intent.GetStringExtra("message");
string title = intent.GetStringExtra("title");
string id = intent.GetStringExtra("id");
var intent2 = new Intent(this, typeof(MainActivity));
intent2.PutExtra("id", id);
intent2.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
NotificationManager notificationManager = (NotificationManager)GetSystemService(Context.NotificationService);
Android.Support.V4.App.NotificationCompat.Builder builder = new Android.Support.V4.App.NotificationCompat.Builder(this)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent)
.SetContentTitle(title)
.SetContentText(message)
.SetSound(RingtoneManager.GetDefaultUri(RingtoneType.Notification))
.SetVibrate(new long[1])
//1 is Hi
.SetPriority(1)
.SetLargeIcon(BitmapFactory.DecodeResource(Resources, SalesFlash.Droid.Resource.Drawable.Icon_lg))
.SetSmallIcon(MyApp.Droid.Resource.Drawable.icon)
.SetChannelId(PRIMARY_NOTIF_CHANNEL);
notificationManager = NotificationManager.FromContext(this);
notificationManager.Notify(0, builder.Build());
wakeLock.Release();
//return StartCommandResult.Sticky;
return base.OnStartCommand(intent, flags, startId);
}
public override void OnDestroy()
{
base.OnDestroy();
}
}
According to this post, a BroadCastReceiver won't work for FCM Notifications. https://stackoverflow.com/a/44287962/5360237
This post seems to show a BroadcastReceiver accepting a Notification.
https://stackoverflow.com/a/45616014/5360237
Any help is much appreciated. Thanks in advance!
Here is the best post that I've read that explains the problem. It also provides a manual solution for the affected devices. https://medium.freecodecamp.org/why-your-push-notifications-never-see-the-light-of-day-3fa297520793 I was able to get Notifications working (when app is closed, stopped or not running) on my Alcatel One Touch Pop by navigating to Settings - Apps and then swiping over to the Restricted tab. From here, I was able to uncheck my app.
NOTE: Messages are delivered to my Android app using the data payload. Even though I was receiving Notifications in the foreground and background, I went ahead and added the following permission to my AndroidManifest.
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
This permission is used for Auto Start. I also used Azure App Center to distribute a signed APK to my test device. Hope this helps someone else.
UPDATE: I removed the RECEIVE_BOOT_COMPLETE permission and did a Debug Build. I closed the app out of Debug (connected to Visual Studio) by swiping it off the screen. I then sent a Notification and it did not display. I then re-opened the app (not being debugged by Visual Studio) and closed it by swiping it off the screen, sent a Notification and it worked. So, it had nothing to with the RECEIVE_BOOT_COMPLETE permission and it doesn't have to be a Release version / signed APK.
I am using FCM to send notifications on Android Device of 8.1 version.
I am trying to achieve the grouping of notifications , and i have achieved it.
Whenever first notification is in status bar and second notification arrives the first notification disappears. From Second notification on wards all the notifications are shown in the group as expected.
How to solve it ?
Posting Code Snipper below.
if ("FirstTime".equals(new SharedPrefsOperations(this).getPreferencesData("ActiveNotifs"))) {
// I AM EXECUTING THIS ONLY AT FIRST TIME , WHEN THE FIRST NOTIFICATION ARRIVES.
String tempData = new SharedPrefsOperations(this).getPreferencesData("ActiveNotifs");
Notification notification = new NotificationCompat.Builder(this, "MYAPP")
.setSmallIcon(R.mipmap.ic_static_notif)
.setContentTitle(content.getTitle())
.setContentText(tempMsg)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setGroup(notifGroup)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setChannelId(channel_id)
.setStyle(inboxStyle)
.setGroupSummary(true)
.build();
notificationManager.notify(id, notification);
new SharedPrefsOperations(this).storePreferencesData("ActiveNotifs", "1");
} else {
// I AM EXECUTING THIS FROM SECOND NOTIFICATION ONWARDS
String tempData = new
SharedPrefsOperations(this).getPreferencesData("ActiveNotifs");
Notification notification = new NotificationCompat.Builder(this, "MYAPP")
.setSmallIcon(R.mipmap.ic_static_notif)
.setContentTitle(content.getTitle())
.setContentText(tempMsg)
.setAutoCancel(true)
.setContentIntent(pendingIntent)
.setGroup(notifGroup)
.setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
.setChannelId(channel_id)
.setStyle(inboxStyle)
.build();
notificationManager.notify(id, notification);
new SharedPrefsOperations(this).storePreferencesData("ActiveNotifs", "1");
}
If you are using same id for notifying, it won't work. You need to use a different id for that
Update
Above Android 8.0 (Oreo) Notification Channels are compulsory. So add the notification channel like this. This should be done before notifying.
val mNotificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
// Android O requires a Notification Channel.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val name = context.getString(R.string.app_name)
// Create the channel for the notification
val mChannel = NotificationChannel("channel_id_sms", name, NotificationManager.IMPORTANCE_DEFAULT)
// Set the Notification Channel for the Notification Manager.
mNotificationManager.createNotificationChannel(mChannel)
}
What is the best way to handle both Notification Messages and Data Messages in firebase using Xamarin Android, while the user is in Foreground and Background?
Also, how do I get the notification data, for example, the text of a particular notification?
PS: I have visited the following threads and none actually helped :
When device screen off then how to handle firebase notification?
Firebase Notification and Data
Display firebase notification data-message on Android tray
Well, I found the answers to my own question so I'm posting the answer for someone who is looking for firebase integration in xamarin.
Install Xamarin.Firebase.Messaging package to your project.
Add the following code to your manifest.xml to receive firebase notifications.
<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>
Now to get the registration token from firebase add a class file and add the following code to it:
[Service]
[IntentFilter(new[] { "com.google.firebase.INSTANCE_ID_EVENT" })]
public class MyFirebaseIIDService : FirebaseInstanceIdService
{
public override void OnTokenRefresh()
{
const string TAG = "MyFirebaseIIDService";
var refreshedToken = FirebaseInstanceId.Instance.Token;
Log.Debug(TAG, "Refreshed token: " + refreshedToken);
SendRegistrationToServer(refreshedToken);
}
void SendRegistrationToServer(string token)
{
// Add custom implementation, as needed.
}
}
Here FirebaseInstanceId.Instance.Token gets the instance token for the current device, Also the method SendRegistrationToServercan be used to send the token to send the token to a server.
Now add another class to handle notifications on foreground
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
// private string TAG = "MyFirebaseMsgService";
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
string messageFrom = message.From;
string getMessageBody = message.GetNotification().Body;
SendNotification(message.GetNotification().Body);
}
void SendNotification(string messageBody)
{
try
{
var intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
var pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.OneShot);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
.SetSmallIcon(Resource.Drawable.ic_stat_ic_notification)
.SetContentTitle("Title")
.SetContentText(messageBody)
.SetAutoCancel(true)
.SetContentIntent(pendingIntent);
NotificationManagerCompat notificationManager = NotificationManagerCompat.From(this);
notificationManager.Notify(0, notificationBuilder.Build());
}
catch (Exception ex)
{
}
}
}
Here the method SendNotification is used to explicitly send a notification to the system tray, Since push notifications while the device is in the foreground are not automatically displayed in the system tray.
When the device is in the background or killed notification is automatically generated and by default the main launcher activity is loaded, to get the data from the background notification you need to use intent as follows(On your main launcher activity):
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);
}
}
Also if google play services are not up to date this code might crash your application so to check if google play services are available or not do this :
public bool IsPlayServicesAvailable()
{
int resultCode = GoogleApiAvailability.Instance.IsGooglePlayServicesAvailable(this);
if (resultCode != ConnectionResult.Success)
{
if (GoogleApiAvailability.Instance.IsUserResolvableError(resultCode))
msgText.Text = GoogleApiAvailability.Instance.GetErrorString(resultCode);
else
{
msgText.Text = "This device is not supported";
Finish();
}
return false;
}
else
{
msgText.Text = "Google Play Services is available.";
return true;
}
}
Check the below link for information on how to add your project to the firebase console:
https://developer.xamarin.com/guides/android/data-and-cloud-services/google-messaging/remote-notifications-with-fcm/\
Update
After the recent changes in the Android Oreo it is mandatory that you add your Notifications into a Channel for that you need to Create a Notification channel in your MainActivity something like below:
void CreateNotificationChannel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.O)
{
// Notification channels are new in API 26 (and not a part of the
// support library). There is no need to create a notification
// channel on older versions of Android.
return;
}
var channel = new NotificationChannel(CHANNEL_ID, "FCM Notifications", NotificationImportance.Default)
{
Description = "Firebase Cloud Messages appear in this channel"
};
var notificationManager = (NotificationManager) GetSystemService(NotificationService);
notificationManager.CreateNotificationChannel(channel);
}
Call this method in you MainActivity's OnCreate method.