For my voluntary music festival app project (Festival Holledau iOS & Android), I need notifications.
I don’t want to built up a backend with registrations and privacy overhead. So I decided to implement on device notifications. There are not much out there. Shiny came later around, but I was finished with my implementation.
I also built up the permission request, now I would use Xamarin.Essentials.
Here is my example implementation which uses the methods CheckIfPushIsAllowed()
and SetPushesAsync()
on each platform. For Android we have to call LocalNotification.Setup()
in the MainActivity.cs file.
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Android.App; using Android.Content; using Android.Content.Res; using Android.OS; using Android.Runtime; using Android.Support.V4.App; using Android.Support.V4.Content; using FestivalHolledauApp.Droid; using FestivalHolledauApp.Models; using Java.Util; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; [assembly: Dependency(typeof(LocalNotification))] namespace FestivalHolledauApp.Droid { public class LocalNotification : ILocalNotification { private static Context Context; public const string CHANNEL_ID = "1"; public static void Setup(Context context) { Context = context; var channelName = "Festival Holledau"; var channel = new NotificationChannel(CHANNEL_ID, channelName, NotificationImportance.Default); NotificationManager notificationManager = context.GetSystemService(Context.NotificationService) as NotificationManager; notificationManager.CreateNotificationChannel(channel); } public async Task SetPushesAsync(IEnumerable<Band> bands) { foreach (var band in bands) { AlarmManager alarmManager = Context.GetSystemService(Context.AlarmService).JavaCast<AlarmManager>(); var timeToDisplay = band.DateTime.ToString("HH:mm"); var message = $"Beginn {timeToDisplay}"; var alarmIntent = new Intent(Context, typeof(AlarmReceiver)); alarmIntent.PutExtra("id", band.Id); alarmIntent.PutExtra("title", band.Name); alarmIntent.PutExtra("message", message); DateTime dateTime; #if DEBUG dateTime = DateTime.Now + new TimeSpan(0, 0, 3); #else int minutesFromSettings = (int)AppSettings.ViewModel.NotificationMinutesBevorBand; dateTime = band.DateTime - new TimeSpan(0, minutesFromSettings, 0); #endif DateTimeOffset dateOffsetValue = DateTimeOffset.Parse(dateTime.ToString()); long millisecondsToBegin = dateOffsetValue.ToUnixTimeMilliseconds(); int bandId = Convert.ToInt32(band.Id); PendingIntent pending = PendingIntent.GetBroadcast(Context, bandId, alarmIntent, PendingIntentFlags.UpdateCurrent); // Add push if (band.IsFavorite && dateTime > DateTime.Now && band.DateTime.Month != 1 && band.DateTime.Day != 1) { alarmManager.Set(AlarmType.RtcWakeup, millisecondsToBegin, pending); } else { pending.Cancel(); } } } public bool IsPushAllowed() { // not needed on Android return true; } public void RequestPermission() { // not needed on Android } } [BroadcastReceiver] public class AlarmReceiver : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { var title = intent.GetStringExtra("title"); var message = intent.GetStringExtra("message"); var idString = intent.GetStringExtra("id"); var id = Convert.ToInt32(idString); Intent resultIntent = new Intent(context, typeof(MainActivity)); resultIntent.SetFlags(ActivityFlags.NewTask | ActivityFlags.ClearTask); const int pendingIntentId = 0; PendingIntent pendingIntent = PendingIntent.GetActivity(context, pendingIntentId, resultIntent, PendingIntentFlags.OneShot); var color = Constants.ColorPrimary.ToAndroid().ToArgb(); NotificationCompat.Builder builder = new NotificationCompat.Builder(context, LocalNotification.CHANNEL_ID) .SetContentTitle(title) .SetContentText(message) .SetDefaults((int)(NotificationDefaults.Sound | NotificationDefaults.Vibrate)) .SetSmallIcon(Resource.Mipmap.icon_notification) .SetColor(color) .SetContentIntent(pendingIntent) .SetPriority((int)NotificationPriority.High); NotificationManager notificationManager = context.GetSystemService(Context.NotificationService) as NotificationManager; Notification notification = builder.Build(); notificationManager.Notify(id, notification); } } }
using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FestivalHolledauApp.iOS; using FestivalHolledauApp.Models; using Foundation; using UIKit; using UserNotifications; using Xamarin.Forms; [assembly: Dependency(typeof(LocalNotification))] namespace FestivalHolledauApp.iOS { public class LocalNotification : ILocalNotification { private bool _alertsAllowed; public LocalNotification() { UNUserNotificationCenter.Current.GetNotificationSettings(CheckIfPushIsAllowed); } void CheckIfPushIsAllowed(UNNotificationSettings settings) { _alertsAllowed = settings.AlertSetting == UNNotificationSetting.Enabled; } public bool IsPushAllowed() { return _alertsAllowed; } public void RequestPermissio