Create a local notification
How to build local notification for Android?
- Create a notification channel by NotificationManager
- Create an Activity intent
- Add title, message,..
- Create a PendingIntent for the above Activity intent
- Create a NotificationCompat.Builder
- Send notification by NotificationManager
// Create a notification channel by NotificationManager private void CreateNotificationChannel() { if (Build.VERSION.SdkInt >= BuildVersionCodes.O) { NotificationImportance importance = NotificationImportance.High; NotificationChannel notificationChannel = new NotificationChannel(mChannelId, mChannelName, importance) { Description = mChannelDescription }; notificationChannel.EnableLights(true); notificationChannel.EnableVibration(true); notificationChannel.SetShowBadge(true); notificationChannel.Importance = NotificationImportance.High; notificationChannel.SetVibrationPattern(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 }); if (mManager != null) { mManager.CreateNotificationChannel(notificationChannel); } } }
public void Send(string title, string message) { try { // Create an Activity intent Intent intent = new Intent(mContext, typeof(MainActivity)); intent.PutExtra(mTitleKey, title); intent.PutExtra(mMessageKey, message); // Create a PendingIntent PendingIntent pendingIntent = PendingIntent.GetActivity(mContext, mPendingIntentId++, intent, PendingIntentFlags.UpdateCurrent); // Create a NotificationCompat.Builder NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, mChannelId) .SetContentIntent(pendingIntent) .SetContentTitle(title) .SetContentText(message) .SetSmallIcon(Resource.Drawable.abc_btn_check_material) .SetVisibility((int)NotificationVisibility.Public) .SetDefaults((int)NotificationDefaults.Sound | (int)NotificationDefaults.Vibrate); // Send notification var notification = builder.Build(); mManager.Notify(mMessageId++, notification); } catch (Exception ex) { // } }
How to build a schedule notification for Android?
- Create a BroadcastReceiver sending a local notification
- Create a BroadcastReceiver intent
- Add title, message,..
- Create a PendingIntent for the above BroadcastReceiver intent
- Setup an AlarmManager to trigger this pending intent at a setting time
// Create a BroadcastReceiver sending a local notification [BroadcastReceiver(Enabled = true, Label = "Local Notifications Broadcast Receiver")] public class AlarmHandler : BroadcastReceiver { public override void OnReceive(Context context, Intent intent) { if (intent?.Extras != null) { string title = intent.GetStringExtra("title"); string message = intent.GetStringExtra("message"); AndroidNotificationManager.mInstance.Show(title, message); } } } ... public class AndroidNotificationManager { public void SendScheduleNotification(string title, string message, DateTime? notifyTime = null) { // Create a BroadcastReceiver intent Intent intent = new Intent(mContext, typeof(AlarmHandler)); intent.PutExtra(mTitleKey, title); intent.PutExtra(mMessageKey, message); // Create a PendingIntent PendingIntent pendingIntent = PendingIntent.GetBroadcast(mContext, mPendingIntentId++, intent, PendingIntentFlags.CancelCurrent); // Setup an AlarmManager to trigger this pending intent long triggerTime = GetNotifyTime(notifyTime.Value); AlarmManager alarmManager = mContext.GetSystemService(Context.AlarmService) as AlarmManager; alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent); } } ...
https://github.com/hung-nb/xamarin-localnotification
How to create daily schedule notification?
Set alarm every Wednesday
// LocalNotification { Title, Message } // HourMinute { mHour, mMinute } // dayOfWeek {1: Sunday, 2: Monday, 3: Tuesday, 4: Wednesday, 5: Thursday, 6: Friday, 7: Saturday} // Example: Send notification at 9:00am every Wednesday: scheduleTime = { mHour: 9, mMinute: 0 }, dayOfWeek = 4 public void SetSchedule(LocalNotification notification, HourMinute scheduleTime, int dayOfWeek) { var appContext = Application.Context; int id = 1234567890; // Create a BroadcastReceiver intent Intent intent = new Intent(appContext, typeof(AlarmBroadcastReceiver)); intent.PutExtra("title", notification.Title); intent.PutExtra("message", notification.Message); // Create a PendingIntent PendingIntent pendingIntent = PendingIntent.GetBroadcast(appContext, id, intent, PendingIntentFlags.CancelCurrent); // Set trigger time var triggerTime = Calendar.Instance; while (triggerTime.Get(CalendarField.DayOfWeek) != dayOfWeek) { // if today is not dayOfWeek, then find next day until find that day // dayOfWeek = Calendar.Wednesday triggerTime.Add(CalendarField.Date, 1); } triggerTime.Set(CalendarField.HourOfDay, scheduleTime.mHour); triggerTime.Set(CalendarField.Minute, scheduleTime.mMinute); triggerTime.Set(CalendarField.Second, 0); Console.WriteLine("+++++++++++++" + triggerTime.Time); // Setup an AlarmManager to trigger this pending intent // Example: Send notification at 9:00am every Wednesday: scheduleTime = { mHour: 9, mMinute: 0 }, dayOfWeek = 4 AlarmManager alarmManager = appContext.GetSystemService(Context.AlarmService) as AlarmManager; alarmManager.SetRepeating(AlarmType.RtcWakeup, triggerTime.TimeInMillis, AlarmManager.IntervalDay * 7, pendingIntent); }
Set alarm from Monday to Friday
Set alarm repeating every day and in on Receive check current day. If its sat or sun ignore it
Create a background service using AlarmManager + BroadcastReceivers
(Deprecated) How to make notification work even when app is killed?
Call local notification (AlarmManager) in a foreground service
- Create a service
[Service] public class AlarmService : Service { IBinder binder; public override IBinder OnBind(Intent intent) { binder = new AlarmServiceBinder(this); return binder; } public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { // this method would hold the code to be run when the service is started. // Call local notification here Send(message, title); return StartCommandResult.NotSticky; } } public class AlarmServiceBinder : Binder { readonly AlarmService service; public AlarmServiceBinder(AlarmService service) { this.service = service; } public AlarmService GetAlarmService() { return service; } }
- Run service in StartForegroundService
public void RunForegroundService() { var intent = new Intent(MainActivity.AppInstance, typeof(AlarmService)); if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.O) { MainActivity.AppInstance.StartForegroundService(intent); } else { MainActivity.AppInstance.StartService(intent); } }
- Add this line into AndroidManifest
<application android:label="XXXXXX" android:usesCleartextTraffic="true"> ... <receiver android:name=".AlarmBroadcastReceiver" android:enabled="true" android:exported="true" /> ... </application>
(New) How to make notification work even when app is killed?
Create a background service
using System; using Android.App; using Android.Content; using Android.Graphics; using Android.OS; using Android.Support.V4.App; using static Android.OS.PowerManager; namespace localnotification.Droid.Notification { [Service(Enabled = true)] public class MyRequestService : Service { private Handler handler; private Action runnable; private bool isStarted; private WakeLock wakeLock; private long DELAY_BETWEEN_LOG_MESSAGES = 15000; private int NOTIFICATION_SERVICE_ID = 999999; private int NOTIFICATION_SERVICE_ALARM_ID = 9999990; private string NOTIFICATION_CHANNEL_ID = "888888"; private string NOTIFICATION_CHANNEL_NAME = "channel_name_123"; public override void OnCreate() { base.OnCreate(); handler = new Handler(); // Here is what you want to do always, i just want to push a notification every 15 seconds here runnable = new Action(() => { DispatchNotificationThatAlarmIsGenerated("I'm running"); handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES); }); } public override StartCommandResult OnStartCommand(Intent intent, StartCommandFlags flags, int startId) { if (isStarted) { // service is already started } else { // Popup start service notification CreateNotificationChannel(); DispatchNotificationThatServiceIsRunning(); // Pop up notification every 15s handler.PostDelayed(runnable, DELAY_BETWEEN_LOG_MESSAGES); isStarted = true; // Keep app work when device sleeps PowerManager powerManager = (PowerManager)this.GetSystemService(Context.PowerService); WakeLock wakeLock = powerManager.NewWakeLock(WakeLockFlags.Full, "Client Lock"); wakeLock.Acquire(); } return StartCommandResult.Sticky; } public override void OnTaskRemoved(Intent rootIntent) { //base.OnTaskRemoved(rootIntent); } public override IBinder OnBind(Intent intent) { // Return null because this is a pure started service. A hybrid service would return a binder that would // allow access to the GetFormattedStamp() method. return null; } public override void OnDestroy() { // Stop the handler. handler.RemoveCallbacks(runnable); // Remove the notification from the status bar. var notificationManager = (NotificationManager)GetSystemService(NotificationService); notificationManager.Cancel(NOTIFICATION_SERVICE_ID); isStarted = false; wakeLock.Release(); base.OnDestroy(); } private void CreateNotificationChannel() { //Notification Channel NotificationChannel notificationChannel = new NotificationChannel(NOTIFICATION_CHANNEL_ID, NOTIFICATION_CHANNEL_NAME, NotificationImportance.Max); notificationChannel.EnableLights(true); notificationChannel.LightColor = Color.Red; notificationChannel.EnableVibration(true); notificationChannel.SetVibrationPattern(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 }); NotificationManager notificationManager = (NotificationManager)this.GetSystemService(Context.NotificationService); notificationManager.CreateNotificationChannel(notificationChannel); } private void DispatchNotificationThatServiceIsRunning() { NotificationCompat.Builder builder = new NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID) .SetDefaults((int)NotificationDefaults.All) .SetSmallIcon(Resource.Drawable.abc_btn_check_material) .SetVibrate(new long[] { 100, 200, 300, 400, 500, 400, 300, 200, 400 }) .SetSound(null) .SetChannelId(NOTIFICATION_CHANNEL_ID) .SetPriority(NotificationCompat.PriorityDefault) .SetAutoCancel(false) .SetContentTitle("Mobile") .SetContentText("My service started") .SetOngoing(true); NotificationManagerCompat notificationManager = NotificationManagerCompat.From(this); //notificationManager.Notify(NOTIFICATION_SERVICE_ID, builder.Build()); StartForeground(NOTIFICATION_SERVICE_ID, builder.Build()); } private void DispatchNotificationThatAlarmIsGenerated(string message) { 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, NOTIFICATION_CHANNEL_ID) .SetSmallIcon(Resource.Drawable.abc_btn_check_material) .SetContentTitle("Alarm") .SetContentText(message) .SetAutoCancel(true) .SetContentIntent(pendingIntent); var notificationManager = (NotificationManager)GetSystemService(NotificationService); notificationManager.Notify(NOTIFICATION_SERVICE_ALARM_ID, notificationBuilder.Build()); } } }
Call service in MainActivity.cs
public void StartMyRequestService() { var serviceToStart = new Intent(this, typeof(MyRequestService)); StartService(serviceToStart); } public void StopMyRequestService() { var serviceToStart = new Intent(this, typeof(MyRequestService)); StopService(serviceToStart); } ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// protected override void OnPause() { base.OnPause(); StartMyRequestService(); } protected override void OnDestroy() { base.OnDestroy(); StartMyRequestService(); } protected override void OnResume() { base.OnResume(); StopMyRequestService(); }
Add permission into Manifest file
<?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.localnotification"> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" /> <application android:label="localnotification.Android" android:theme="@style/MainTheme"></application> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> </manifest>
How to re-schedule notification after reboot?
Hold RECEIVE_BOOT_COMPLETED
and have a BroadcastReceiver
to receive the boot so that if the phone is rebooted you can re-schedule your alarms.
Create background service using WorkManager
WorkManager is a library that makes it easy to schedule deferrable, asynchronous tasks even if the app exits or the device restarts.
It was designed to be backwards compatible to API 14 and does so by wrapping JobScheduler, AlarmManager, and BroadcastReceivers all in one.
Using JobScheduler your app will be running on a device that is API 23+. Anything below, you’ll be using a combination of AlarmManager + BroadcastReceivers.
But the minimum interval for PeriodicWork is 15 minutes (same as JobScheduler Periodic Job).
If you need it every 30 seconds — consider using OneTimeWork and schedule it again when the work is done.
But
Approximately your location query every 30 seconds will responsible for about 10% battery drain.
Leave a Reply