diff --git a/src/Plugin.LocalNotifications.Android/LocalNotificationsImplementation.cs b/src/Plugin.LocalNotifications.Android/LocalNotificationsImplementation.cs index 4477ddc..3ae99b8 100644 --- a/src/Plugin.LocalNotifications.Android/LocalNotificationsImplementation.cs +++ b/src/Plugin.LocalNotifications.Android/LocalNotificationsImplementation.cs @@ -6,6 +6,7 @@ using System.IO; using System.Xml.Serialization; using Android.OS; +using Android.App.Job; namespace Plugin.LocalNotifications { @@ -97,13 +98,60 @@ public void Show(string title, string body, int id, DateTime notifyTime) } var serializedNotification = SerializeNotification(localNotification); - intent.PutExtra(ScheduledAlarmHandler.LocalNotificationKey, serializedNotification); + - var pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, intent, PendingIntentFlags.CancelCurrent); - var triggerTime = NotifyTimeInMilliseconds(localNotification.NotifyTime); - var alarmManager = GetAlarmManager(); + - alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent); + if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop) + { + Java.Lang.Class javaClass = Java.Lang.Class.FromType(typeof(ScheduledJobHandler)); + ComponentName component = new ComponentName(Application.Context, javaClass); + + // Bundle up parameters + var extras = new PersistableBundle(); + extras.PutString(ScheduledAlarmHandler.LocalNotificationKey, serializedNotification); + extras.PutInt(ScheduledJobHandler.LocalNotificationIconId, NotificationIconId); + + var triggerTime = NotifyTimeInMilliseconds(localNotification.NotifyTime) - NotifyTimeInMilliseconds(DateTime.Now); + + JobInfo.Builder builder = new JobInfo.Builder(id, component) + .SetMinimumLatency(triggerTime) // Fire at TriggerTime + .SetOverrideDeadline(triggerTime + 5000) // Or at least 5 Seconds Later + .SetExtras(extras) + .SetPersisted(CheckBootPermission()); //Job will be recreated after Reboot if Permissions are granted + JobInfo jobInfo = builder.Build(); + + JobScheduler jobScheduler = GetJobScheduler(); + + int result = jobScheduler.Schedule(jobInfo); + + if (result == JobScheduler.ResultSuccess) + { + // The job was scheduled. So nothing more to do + } + else + { + // The job wasn´t scheduled. So just use the old implementation? + triggerTime = NotifyTimeInMilliseconds(localNotification.NotifyTime); + intent.PutExtra(ScheduledAlarmHandler.LocalNotificationKey, serializedNotification); + + var pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, intent, PendingIntentFlags.CancelCurrent); + + var alarmManager = GetAlarmManager(); + + alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent); + } + } + else + { + intent.PutExtra(ScheduledAlarmHandler.LocalNotificationKey, serializedNotification); + + var pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, intent, PendingIntentFlags.CancelCurrent); + + var alarmManager = GetAlarmManager(); + var triggerTime = NotifyTimeInMilliseconds(localNotification.NotifyTime); + alarmManager.Set(AlarmType.RtcWakeup, triggerTime, pendingIntent); + } } /// @@ -112,14 +160,22 @@ public void Show(string title, string body, int id, DateTime notifyTime) /// Id of the notification to cancel public void Cancel(int id) { - var intent = CreateIntent(id); - var pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, intent, PendingIntentFlags.CancelCurrent); + if (Build.VERSION.SdkInt >= BuildVersionCodes.Lollipop) + { + JobScheduler jobScheduler = GetJobScheduler(); + jobScheduler.Cancel(id); + } + else + { + var intent = CreateIntent(id); + var pendingIntent = PendingIntent.GetBroadcast(Application.Context, 0, intent, PendingIntentFlags.CancelCurrent); - var alarmManager = GetAlarmManager(); - alarmManager.Cancel(pendingIntent); + var alarmManager = GetAlarmManager(); + alarmManager.Cancel(pendingIntent); - var notificationManager = NotificationManagerCompat.From(Application.Context); - notificationManager.Cancel(id); + var notificationManager = NotificationManagerCompat.From(Application.Context); + notificationManager.Cancel(id); + } } private Intent CreateIntent(int id) @@ -135,6 +191,24 @@ private AlarmManager GetAlarmManager() return alarmManager; } + private JobScheduler GetJobScheduler() + { + var jobScheduler = Application.Context.GetSystemService(Context.JobSchedulerService) as JobScheduler; + return jobScheduler; + } + + private bool CheckBootPermission() + { + if (Build.VERSION.SdkInt >= BuildVersionCodes.M) + { + return Application.Context.CheckSelfPermission("android.permission.RECEIVE_BOOT_COMPLETED") == Android.Content.PM.Permission.Granted; + } + else + { + return Android.Support.V4.Content.PermissionChecker.CheckSelfPermission(Application.Context, "android.permission.RECEIVE_BOOT_COMPLETED") == Android.Support.V4.Content.PermissionChecker.PermissionGranted; + } + } + private string SerializeNotification(LocalNotification notification) { var xmlSerializer = new XmlSerializer(notification.GetType()); @@ -154,4 +228,4 @@ private long NotifyTimeInMilliseconds(DateTime notifyTime) return utcAlarmTimeInMillis; } } -} \ No newline at end of file +} diff --git a/src/Plugin.LocalNotifications.Android/Plugin.LocalNotifications.Android.csproj b/src/Plugin.LocalNotifications.Android/Plugin.LocalNotifications.Android.csproj index 322f099..94d02e5 100644 --- a/src/Plugin.LocalNotifications.Android/Plugin.LocalNotifications.Android.csproj +++ b/src/Plugin.LocalNotifications.Android/Plugin.LocalNotifications.Android.csproj @@ -14,9 +14,9 @@ 512 Resources\Resource.Designer.cs Off - true v8.1 - + + true @@ -57,6 +57,7 @@ + diff --git a/src/Plugin.LocalNotifications.Android/Resources/Resource.Designer.cs b/src/Plugin.LocalNotifications.Android/Resources/Resource.Designer.cs index 04f3fd5..5389127 100644 --- a/src/Plugin.LocalNotifications.Android/Resources/Resource.Designer.cs +++ b/src/Plugin.LocalNotifications.Android/Resources/Resource.Designer.cs @@ -1,11 +1,11 @@ #pragma warning disable 1591 //------------------------------------------------------------------------------ // -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 +// Dieser Code wurde von einem Tool generiert. +// Laufzeitversion:4.0.30319.42000 // -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. +// Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn +// der Code erneut generiert wird. // //------------------------------------------------------------------------------ diff --git a/src/Plugin.LocalNotifications.Android/ScheduledJobHandler.cs b/src/Plugin.LocalNotifications.Android/ScheduledJobHandler.cs new file mode 100644 index 0000000..ead7ea3 --- /dev/null +++ b/src/Plugin.LocalNotifications.Android/ScheduledJobHandler.cs @@ -0,0 +1,49 @@ +using System.IO; +using System.Xml.Serialization; + +using Android.App; +using Android.App.Job; + +namespace Plugin.LocalNotifications +{ + [Service(Name = "plugin.localnotifications.ScheduledJobHandler", Permission = "android.permission.BIND_JOB_SERVICE")] + public class ScheduledJobHandler : JobService + { + /// + /// + /// + public const string LocalNotificationIconId = "LocalNotificationIconId"; + + /// + /// + /// + /// + /// + public override bool OnStartJob(JobParameters jobParams) + { + var extra = jobParams.Extras.GetString(ScheduledAlarmHandler.LocalNotificationKey); + var notificationIconId = jobParams.Extras.GetInt(LocalNotificationIconId); + var notification = DeserializeNotification(extra); + + LocalNotificationsImplementation.NotificationIconId = notificationIconId; + CrossLocalNotifications.Current.Show(notification.Title, notification.Body, notification.Id); + return true; + } + + public override bool OnStopJob(JobParameters jobParams) + { + // Called by Android when it has to terminate a running service. + return false; // Don't reschedule the job. + } + + private LocalNotification DeserializeNotification(string notificationString) + { + var xmlSerializer = new XmlSerializer(typeof(LocalNotification)); + using (var stringReader = new StringReader(notificationString)) + { + var notification = (LocalNotification)xmlSerializer.Deserialize(stringReader); + return notification; + } + } + } +}