Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using System.Xml.Serialization;
using Android.OS;
using Android.App.Job;

namespace Plugin.LocalNotifications
{
Expand Down Expand Up @@ -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);
}
}

/// <summary>
Expand All @@ -112,14 +160,22 @@ public void Show(string title, string body, int id, DateTime notifyTime)
/// <param name="id">Id of the notification to cancel</param>
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)
Expand All @@ -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());
Expand All @@ -154,4 +228,4 @@ private long NotifyTimeInMilliseconds(DateTime notifyTime)
return utcAlarmTimeInMillis;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@
<FileAlignment>512</FileAlignment>
<AndroidResgenFile>Resources\Resource.Designer.cs</AndroidResgenFile>
<GenerateSerializationAssemblies>Off</GenerateSerializationAssemblies>
<AndroidUseLatestPlatformSdk>true</AndroidUseLatestPlatformSdk>
<TargetFrameworkVersion>v8.1</TargetFrameworkVersion>
<AndroidTlsProvider></AndroidTlsProvider>
<AndroidTlsProvider>
</AndroidTlsProvider>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
Expand Down Expand Up @@ -57,6 +57,7 @@
<Compile Include="Resources\Resource.Designer.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ScheduledAlarmHandler.cs" />
<Compile Include="ScheduledJobHandler.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

49 changes: 49 additions & 0 deletions src/Plugin.LocalNotifications.Android/ScheduledJobHandler.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
///
/// </summary>
public const string LocalNotificationIconId = "LocalNotificationIconId";

/// <summary>
///
/// </summary>
/// <param name="jobParams"></param>
///
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;
}
}
}
}