Conditional handling Firebase push notifications - c#

I have a Xamarin.Forms app that supports Android and iPhone. The app sends and receives push notifications using Firebase for Android and Azure Notification Hub for iOS. If the app sends a notification from the sender's phone, multiple people in the group receive it and can handle it. But it is not what we need. I want only the first receiver to handle the notification, and the others should ignore it. Is there a good way to implement it?
Here is my code that handles push notifications on Android:
[Service]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class FirebaseService : FirebaseMessagingService
{
public override void OnMessageReceived(RemoteMessage message)
{
try
{
base.OnMessageReceived(message);
string messageBody = message.GetNotification()?.Body;
VideoCallMessage videoCallMessage = JsonConvert.DeserializeObject<VideoCallMessage>(messageBody);
if (App.UserContext.IsEmployee)
{
// convert the incoming message to a local notification
SendLocalNotification(messageBody);
}
}
catch (Exception ex)
{
Log.Debug("FirebaseService.OnMessageReceived()", $"Exception in OnMessageReceived(). ErrorMessage: {ex.Message}, Stack Trace: {ex.StackTrace}");
Microsoft.AppCenter.Crashes.Crashes.TrackError(ex); // Report the exception to App Center
}
}
For iOS in Delegate.cs:
[Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
completionHandler();
NSDictionary userInfo = response.Notification.Request.Content.UserInfo;
ProcessNotification(userInfo);
}
[Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
completionHandler(UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Alert);
NSDictionary userInfo = notification.Request.Content.UserInfo;
ProcessNotification(userInfo);
}

Related

Xamarin forms not updating notifications changes on final APK

I have a Xamarin Forms project where I use the following notifications code for Android:
[Service(Exported = false)]
[IntentFilter(new[] { "com.google.firebase.MESSAGING_EVENT" })]
public class MyFirebaseMessagingService : FirebaseMessagingService
{
public override void OnMessageReceived(RemoteMessage message)
{
base.OnMessageReceived(message);
string messageTitle = GetTitle(message);
string messageBody = GetBody(message);
// convert the incoming message to a local notification
ConfigureAndSendLocalNotification(messageTitle, messageBody);
}
private void ConfigureAndSendLocalNotification(string title, string body)
{
NotificationCompat.Builder notificationBuilder = ConfigureNotificationBuilder(title, body);
SendNotification(notificationBuilder);
}
private NotificationCompat.Builder ConfigureNotificationBuilder(string title, string body)
{
Intent intent = new Intent(this, typeof(MainActivity));
intent.AddFlags(ActivityFlags.ClearTop);
intent.PutExtra("title", title);
intent.PutExtra("message", body);
PendingIntent pendingIntent = PendingIntent.GetActivity(this, 0, intent, PendingIntentFlags.Immutable);
NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this, Util.Constants.NotificationChannelName)
...//configuring notification
return notificationBuilder;
}
I'm trying to modify this code to give users a better experience. However, when i generate the APK (going to archive -> distribute -> save as), it seems not to recognize any change in this file.
I've actually commented this part of the code:
public override void OnMessageReceived(RemoteMessage message){
//base.OnMessageReceived(message);
string messageTitle = GetTitle(message);
string messageBody = GetBody(message);
// convert the incoming message to a local notification
//ConfigureAndSendLocalNotification(messageTitle, messageBody);
}
And the notification keeps appearing on the generated APK when I run it on my Android.
Creepy detail: In the Android emulator, all of my changes are correctly displayed. They really are just not reflected in the final APK.
Any ideas?

C# Telegram Bot Notification

Is there a way in C# to send a notification to a user (personal message) from a bot? I only found solutions to send messages to a channel/group where my bot is added with Admin rights.
I want to send notifications - if allowed - from my system to the user when logged in / system notifications.
Later on I want to enable the user to send commands to this bot to action some key functionalities in the system - such as, but not limited to, adding new users. With the necessary administrator rights of course.
I am posting this answer as I solved my own question after several hours of researching and testing.
First off, I created a EventHandler class to handle all incoming messages to my Telegram bot:
public class TelegramBotEvents
{
private readonly TelegramBotClient _telegramBotClient;
private readonly ODWorkflowDBContext _context;
public TelegramBotEvents(
ODWorkflowDBContext context,
TelegramBotClient telegramBotClient
)
{
this._context = context ?? throw new ArgumentNullException(nameof(context));
this._telegramBotClient = telegramBotClient ?? throw new ArgumentNullException(nameof(telegramBotClient));
#pragma warning disable CS0618 // Type or member is obsolete. TODO: Implement Polling
this._telegramBotClient.StartReceiving();
this._telegramBotClient.OnMessage += (sender, eventArg) =>
{
if (eventArg.Message != null)
{
MessageReceive(eventArg.Message);
}
};
#pragma warning restore CS0618 // Type or member is obsolete. TODO: Implement Polling
}
public void MessageReceive(Message message)
{
try
{
if (message.Type == MessageType.Text)
{
if (message.Text.ToLower() == "/register")
{
var userThirdPartyNotification = this._context.UserThirdPartyNotifications.FirstOrDefault(e =>
e.UserThirdPartyNotificationActive &&
e.UserThirdPartyNotificationUserName == message.From.Username);
userThirdPartyNotification.UserThirdPartyNotificationChatId = message.From.Id;
this._context.UserThirdPartyNotifications.Update(userThirdPartyNotification);
this._context.SaveChanges();
this._telegramBotClient.SendTextMessageAsync(message.From.Id, "You have successfully opted in to receive notifications via Telegram.");
}
}
else
{
this._telegramBotClient.SendTextMessageAsync(message.From.Id, $"\"{message.Text}\" was not found. Please try again.");
}
}
catch (Exception ex)
{
this._telegramBotClient.SendTextMessageAsync(message.From.Id, $"Failure processing your message: {ex.Message}");
}
}
}
After the above I Injected this class in my Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
#region Dependency_Injections
services.AddScoped<IPasswordEncrypt, PasswordEncrypt>();
services.AddScoped<IHelperFunctions, HelperFunctions>();
services.AddScoped<IEncryptor, Encryptor>();
services.AddTransient(typeof(ILogging<>), typeof(Logging<>));
services.AddSingleton(new TelegramBotClient(Configuration["ArtificialConfigs:TelegramBotToken"]));
services.AddScoped(p =>
new TelegramBotEvents(
(DBContext)p.GetService(typeof(DBContext)),
(TelegramBotClient)p.GetService(typeof(TelegramBotClient))));
#endregion
services.AddControllersWithViews();
services.AddRazorPages();
}
After these two steps I could capture the client id from the user that sent any message to my bot.
Once any user logins - with permissions granted inside the web system - they will receive personal messages from my bot inside Telegram.

Actionable push notification in Xamarin iOS

var acceptAction = UNNotificationAction.FromIdentifier("AcceptAction", "Accept", UNNotificationActionOptions.None);
var declineAction = UNNotificationAction.FromIdentifier("DeclineAction", "Decline", UNNotificationActionOptions.None);
// Create category
var meetingInviteCategory = UNNotificationCategory.FromIdentifier("MeetingInvitation",
new UNNotificationAction[] { acceptAction, declineAction }, new string[] { }, UNNotificationCategoryOptions.CustomDismissAction);
// Register category
var categories = new UNNotificationCategory[] { meetingInviteCategory };
UNUserNotificationCenter.Current.SetNotificationCategories(new NSSet<UNNotificationCategory>(categories));
how can you receive a custom actionable push notification and where need to put the above code in which file?
Before an iOS app can send notifications to the user the app must be registered with the system and, because a notification is an interruption to the user, an app must explicitly request permission before sending them.
Notification permission should be requested as soon as the app launches by adding the following code to the FinishedLaunching method of the AppDelegate and setting the desired notification type (UNAuthorizationOptions):
...
using UserNotifications;
...
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
....
//after iOS 10
if(UIDevice.CurrentDevice.CheckSystemVersion(10,0))
{
UNUserNotificationCenter center = UNUserNotificationCenter.Current;
center.RequestAuthorization(UNAuthorizationOptions.Alert | UNAuthorizationOptions.Sound | UNAuthorizationOptions.UNAuthorizationOptions.Badge, (bool arg1, NSError arg2) =>
{
});
center.Delegate = new NotificationDelegate();
}
else if(UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var settings = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Alert| UIUserNotificationType.Badge| UIUserNotificationType.Sound,new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
}
return true;
}
New to iOS 10, an app can handle Notifications differently when it is in the foreground and a Notification is triggered. By providing aUNUserNotificationCenterDelegate and implementing theUserNotificationCentermethod, the app can take over responsibility for displaying the Notification. For example:
using System;
using ObjCRuntime;
using UserNotifications;
namespace xxx
{
public class NotificationDelegate:UNUserNotificationCenterDelegate
{
public NotificationDelegate()
{
}
public override void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
{
// Do something with the notification
Console.WriteLine("Active Notification: {0}", notification);
// Tell system to display the notification anyway or use
// `None` to say we have handled the display locally.
completionHandler(UNNotificationPresentationOptions.Alert|UNNotificationPresentationOptions.Sound);
}
public override void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action completionHandler)
{
// Take action based on Action ID
switch (response.ActionIdentifier)
{
case "reply":
// Do something
break;
default:
// Take action based on identifier
if (response.IsDefaultAction)
{
// Handle default action...
}
else if (response.IsDismissAction)
{
// Handle dismiss action
}
break;
}
// Inform caller it has been handled
completionHandler();
}
}
}

How is the device able to receive notifications even though SBNotificationHub.RegisterNativeAsync() is never called?

This code results in the device receiving a test notification, but there's no call to RegisterNativeAsync unless there's an error. Thus, how does the hub know about the device?
[Register("AppDelegate")]
public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
{
SBNotificationHub Hub { get; set; }
public const string ConnectionString = "Endpoint=xxx";
public const string NotificationHubPath = "xxx";
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary launchOptions)
{
var settings = UIUserNotificationSettings.GetSettingsForTypes(UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, new NSSet());
UIApplication.SharedApplication.RegisterUserNotificationSettings(settings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
global::Xamarin.Forms.Forms.Init();
LoadApplication(new App());
return base.FinishedLaunching(uiApplication, launchOptions);
}
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
// Create a new notification hub with the connection string and hub path
Hub = new SBNotificationHub(ConnectionString, NotificationHubPath);
// Unregister any previous instances using the device token
Hub.UnregisterAllAsync(deviceToken, (error) =>
{
if (error != null)
{
// Error unregistering
return;
}
// Register this device with the notification hub
Hub.RegisterNativeAsync(deviceToken, null, (registerError) =>
{
if (registerError != null)
{
// Error registering
}
});
});
}
}
According to RegisteredForRemoteNotifications - Xamarin, this method has nothing to do with registering itself.1 As far a I can tell from RegisteredForRemoteNotifications never triggered — Xamarin Forums, applications are supposed to override it, and it serves as a handler that is invoked after the user allows the application to receive push notifications.
In fact, the code you've given is your code, not library's.
1And examining the source code of UnregisterAllAsync in ILSpy decompilation of Xamarin.Azure.NotificationHubs.iOS.dll confirms as such.

Xamarin - null reference exception while trying to display a toast or start an activity from a service

Both trying to display a toast and to start a new activity fail. There HAS to be a way to make this work. Is there a way to notify the UI about something happening, an event or something?
Right now I am only able to log the info about the messages to console output.
The context itself is not null, but something else, possibly related to it, is causing the null reference exception.
Here's my code:
[Service(Exported = false), IntentFilter(new[] { "com.google.android.c2dm.intent.RECEIVE" })]
class MyGcmListenerService : GcmListenerService
{
public override void OnMessageReceived(string from, Bundle data)
{
string msg = data.GetString("message");
// this fails
Toast.MakeText(this, msg, ToastLength.Long).Show();
// this fails too
Intent pa = new Intent(this, typeof(PopupActivity));
pa.AddFlags(ActivityFlags.NewTask);
StartActivity(pa);
}
}
This is a context Error. This is null therefore you get a null exception pointer. It is null because it is inside a service and not an activity.
You should try to use Application.Context instead of this. It is a static in Xamarin.Droid and should return the context.
(Note that I can't test is atm)
I resolved this issue with Xamarin.Forms and MessagingCenter.
Here's my service:
[Service(Exported = false), IntentFilter(new[] { "com.google.android.c2dm.intent.RECEIVE" })]
class MyGcmListenerService : GcmListenerService
{
public override void OnMessageReceived(string from, Bundle data)
{
string msg = data.GetString("message");
// send a string via Xamarin MessagingCenter
MessagingCenter.Send<object, string>(this, "ShowAlert", msg);
}
}
And here's part of my PCL App class constructor:
// subscribe to the messages
MessagingCenter.Subscribe<object, string>(this, "ShowAlert", (s, msg) =>
{
// run on UI thread
Device.BeginInvokeOnMainThread(() =>
{
MainPage.DisplayAlert("Push message", msg, "OK");
});
});

Categories