cordova-plugin-local-notifications
cordova-plugin-local-notifications copied to clipboard
Trigger notifcation happen multiple times
Your Environment
- Plugin version: lastest
- Platform: Android
- OS version: 7.1
- Device manufacturer / model: Samsung Galaxy S7 Edge
- Cordova version: 7.1.0
- Cordova platform version: android 6.3.0
- Plugin config: trigger schedule
- Ionic Version (if using Ionic): 1.3.0
Expected Behavior
Notification should happen once only on trigger time
Actual Behavior
Notifcation non-stop fire repeatedly in about 5 minutes then stop
Steps to Reproduce
This is my code in app.js. I use ionic v1 with ngCordova
$ionicPlatform.ready(function() {
cordova.plugins.notification.local.schedule({
id: 1,
title: 'test title',
text: 'test description',
trigger: { every: {hour: 10, minute: 0, second: 0} }
});
Context
Fire notication at specific hour every day like 10 am and 4pm every day
Debug logs
Include Android logs
12-14 04:06:04.015 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.017 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.018 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.019 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.020 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.021 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.022 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.023 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.024 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.025 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
12-14 04:06:04.026 30007-30007/io.ionic.starter D/CordovaWebViewImpl: >>> loadUrl(javascript:cordova.plugins.notification.local.core.fireEvent("trigger",{"id":1,"title":"test title","trigger":{"every":{"hour":4,"minute":5,"second":0},"type":"calendar"},"actions":[],"attachments":[],"autoClear":true,"defaults":0,"foreground":false,"groupSummary":false,"launch":true,"led":true,"lockscreen":true,"number":0,"priority":0,"progressBar":{"enabled":false,"value":0,"maxValue":100,"indeterminate":false},"showWhen":true,"silent":false,"smallIcon":"res:\/\/icon","sound":true,"text":"","vibrate":false,"wakeup":true,"meta":{"plugin":"cordova-plugin-local-notifications","version":"0.9-beta"}},{"event":"trigger","foreground":true,"queued":false,"notification":1}))
A lot of them
It happen when I use every property even like this
cordova.plugins.notification.local.schedule({
id: 10000,
title: 'test title',
trigger: {every: 'minute', firstAt: new Date(2017, 11, 15, 17, 12)}
});
Same issue here.
Ionic Framework: 3.9.2 Cordova version: 7.1.0 Cordova Android version: android 6.3.0
Device: Samsung Galaxy S7 (SM-G930F) Android Version: 7.0
Plugin version: v0.9.0-beta.2
The point Repeating
in the README.md
says:
-
Or trigger every time the date matches
So it means every time the date matches. If I set the hour
to 10
and minute
to 30
, it will repeat the notification a minute long, until the date changed to hour
10
and minute
31
.
This ends up in spamming / flooding notifications, until the matched time ends up. Even if I set the second
to 0
, it spams / floods notifications, until the minute ends up.
If any further information is required, please let me know.
Cheers Unkn0wn0x
Additional Informations from a Firebase Crash Reporting (thanks to Firebase), because the app doesn't responded anymore / crashed.
API-Level: 24
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer at de.appplant.cordova.plugin.notification.Request.getMatchingComponents(Request.java:219)
- https://github.com/katzer/cordova-plugin-local-notifications/blob/69810d81ff3c9d7481fcd373254f476e5f301085/src/android/notification/Request.java#L219
Exception java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
de.appplant.cordova.plugin.notification.Request.getMatchingComponents (Request.java:219)
de.appplant.cordova.plugin.notification.Request.buildTrigger (Request.java:158)
de.appplant.cordova.plugin.notification.Request.<init> (Request.java:75)
de.appplant.cordova.plugin.localnotification.LocalNotification.schedule (LocalNotification.java:270)
de.appplant.cordova.plugin.localnotification.LocalNotification.access$400 (LocalNotification.java:60)
de.appplant.cordova.plugin.localnotification.LocalNotification$1.run (LocalNotification.java:145)
java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1133)
java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:607)
java.lang.Thread.run (Thread.java:762)
Issue type, rated by Firebase is fatal.
@haha8x I tried with your code and it works as expected (tested with 0.9-beta.2)
@Unkn0wn0x
Please provide a sample code which reproduces the crash/behavor.
@katzer
My fault, sorry, here You go:
/** create a new array, which will hold all notification configurations */
let notifications: Array<any> = new Array();
/** create a single notification configuration */
const notificationConfig: Object = {
id: 1,
title: 'my title',
text: 'my message',
foreground: true
trigger: {
every: {
weekday: 1,
hour: 10,
minute: 30,
second: 0
}
}
};
/** push the single notification configuration into the notifications array */
notificationConfig.push(notificationConfig):
/** schedule the notifications array */
this.notificationCtrl.schedule(notifications);
If I open the console on my hybrid app and run the function
cordova.plugins.notification.local.getScheduled(function(data) {
console.log(data);
});
The newly created and scheduled notification appears as expected.
Here are some more details from the firebase crash report. As I expect, You are german, I hope it's okay, if the screenshot is in german.
Greetings from Munich :wave:
@haha8x
I tried out the code without being able to reproduce that bug. It worked well... Are you interfacing with the plugin in direct way or through some (Ionic native) wrapper?
To be able to get the same error message I need to assign a string instead of an integer. But thats not the plugins fault.
const notificationConfig: Object = {
id: 1,
title: 'my title',
text: 'my message',
foreground: true,
trigger: {
every: {
weekday: 1,
hour: 10,
minute: "30" // <- Would throw a ClassCastException
}
}
};
@katzer
I think You mean me instead of haha8x, or?
You're right. I'm using the ionic native wrapper:
- https://ionicframework.com/docs/native/local-notifications/
- https://github.com/ionic-team/ionic-native/tree/50d7762b8a6e3818fad48a8860900d1c1e76ba6b/src/%40ionic-native/plugins/local-notifications
Yep, I've noticed already, that a string would throw a ClassCastExpection.
I think, I found the "problem" in the ionic native wrapper. Take a look at the following line:
- https://github.com/ionic-team/ionic-native/blob/50d7762b8a6e3818fad48a8860900d1c1e76ba6b/src/%40ionic-native/plugins/local-notifications/index.ts#L28
The ionic native wrapper expects a string for every
, but I'm passing an object into it.
Also I found some bugs in the ionic native repository, related to Your plugin:
- https://github.com/ionic-team/ionic-native/search?q=local-notification&type=Issues&utf8=%E2%9C%93
especially this bug:
- https://github.com/ionic-team/ionic-native/issues/2113#issuecomment-357157051
where it seems like the user has the same behaviour.
So I think it would be the correct way to use Your plugin without the ionic native wrapper.
I'll let You know today evening, if it worked or not.
Cheers Unkn0wn0x
Hi, I have the same issue on Android. Works perfectly on iOS.
Scheduling works and is repeated every match, but the notification is triggered over and over again the whole minute.
I am using Ionic but use the plugin directly since Ionic still use an old version of the plugin..
Ionic: 3.19.1 Cordova: 7.1.0 Cordova Platforms: android 7.0.0 Cordova Android version: android 6.3.0
Plugin version: v0.9.0-beta.2
cordova.plugins.notification.local.schedule({
id: 1,
title: 'Hello',
text: 'World',
trigger: {
every: {
weekday: 1,
hour: 10,
minute: 30
}
},
})
@katzer
I can reproduce the same behaviour as @stinaa described.
- Open Chrome WebView in
chrome://inspect
- Run directly from the console the following function:
cordova.plugins.notification.local.schedule({
id: 100,
title: 'my title',
text: 'my message',
foreground: true,
trigger: {
every: {
weekday: 2,
hour: 20,
minute: 45,
second: 0
}
}
});
I tried to listen on the triggered
event, but the WebView instantly stops to respond, after the first notification was pushed.
cordova.plugins.notification.local.on('trigger', function(data) {
console.log('triggered', data);
});
It fires notifications until the app does not respond anymore and then finally crashes. Even if the app was killed, it fires the scheduled notifications further on.
Cheers Unkn0wn0x
--- edit ---
Okay, I've noticed another really curious behaviour.
If I set the trigger to early from the point, where I schedule the notification, it fires the notification the whole time. Tested with ~ one (1) minute.
On the other hand, if I let a little "delay" between the schedule and the trigger-time, tested with two (2) minutes, it works like a charm.
@katzer Do You have an explanation for that? :thinking:
Now I could reproduce. It happens when the components matches now and no count
is specified.
Should be fixed now. Please reopen otherwise. You can use the master branch version or set count:
as a workaround.
There's no second
property for every
- It has always a zero value.
@katzer Tested with Your latest fix, works like a charm.
I really appreciate Your fast reply and hard work on this plugin!
Cheers Unkn0wn0x
Hey @katzer, thanks for your quick reply and fix!
I tried to add count
and it works perfectly if I schedule the notification to trigger immediately (only trigger once), but if I want it to trigger in a few minutes, it doesn't work. Tried with 2 minutes and 5 minutes. Still working on iOS though.
Tried both with v 0.9.0-beta.2 and with latest version (commit 63c99d3ea1eb9a902fb9f1d121bdd99a243a45eb)
Example:
cordova.plugins.notification.local.schedule({
id: 100,
foreground: true,
title: 'my title',
text: 'my text',
trigger: {
count: 1,
every: { weekday: 4, hour: 10, minute: 28 }
}
})
This would work if I make the scheduling at Thursday 10.28, will trigger immediately. But if I schedule it at Thursday 10.27 (for 10.28), it is not triggered.
Wooooh . Same issue .
every: { weekday: 1, hour: 22, minute: 12 }
this floods my device with notifications !
p.s : at
and firstAt
is not specified
@xProgrammer-007 I tried yours using master without any flood. It calculates correct trigger dates. Also if the relative date points no now which was the reason for the "flood" before.
@stinaa I adapter your code to point to a minute ahead from now but cannot reproduce any issue. The correct date is calculated and the notification is triggered as expected.
You need to use 0.9-beta.3 or at least these commit.
Just use : trigger: { every: { hour: 20, minute: 20, second: 15 }, count: 1 },
it will be fired only once at the right time.
Hope it works 4 u too!
I came across this issue today on an HTC M9 running Android 7.0 with . While adding count will stop the spamming it does not allow the notification to have any repeats (using every doesn't work when you add count). an example where adding count fails to solve the problem:
cordova.plugins.notification.local.schedule({
title: 'message',
trigger: {firstAt: time2, every: "minute", count:1},
foreground: true,
id: 2
});
In this case the notification is only fired once and then notifications don't get repeated every minute afterwards. I would suggest reopening this issue because:
-
the "just add count" workaround is not a slution to this problem -> it dosen't work in all uses cases.
-
Commit b93cf65 which is marked as the fix for this issue dose not solve this issue either.
I have the the same issue with @JoueBien . Is there any solution to this ?
@JoueBien and @mschristo , I was having the same problem here, so I changed "count" for 30, the multiple notifications didn't happen and it repeated for the next 2 days (Didn't test more). Probably will repeat for the next 30 days. One trick could be rescheduling the notification every time the user opens the app. Actually this is a nice feature to not repeat infinitely if the app is not being used. 🗡
Same issue here every: { hour: 16, minute: 11} notification comes multiples times at 16.11
I have another confirmed case of this.
The ionic wrapper (@ionic-native/local-notifications v4.17.0
) is not compatible with the latest versions of this plugin (beta 2 or beta 3). As a result, we are using the cordova plugin directly.
When using the cordova plugin directly:
- using
every: { minute: 55 }
will fire notifications indefinitely while the clock reads "xx:55" - using
count: 1
will only fire 1 notification, but it prevents the recurrence
If we come up with a solution, I will be sure to update this post. It is maddening how fragile this ionic/cordova universe is.
Edit:
The unfortunate solution we came up with was reverting the ionic plugin AND this plugin back to old versions. We can now use the ionic plugin, and the notifications are recurring as expected.
"@ionic-native/local-notifications": "4.6"
"de.appplant.cordova.plugin.local-notification": "^0.8.5"
The unfortunate solution we came up with was reverting the ionic plugin AND this plugin back to old versions. We can now use the ionic plugin, and the notifications are recurring as expected.
"@ionic-native/local-notifications": "4.6" "de.appplant.cordova.plugin.local-notification": "^0.8.5"
I revert my dependencies to those versions but... Fuc*sh :(
WORK
let options = {
id: this.idGenerator.generate(),
text: this.timeToMeditateNotificationText,
sound: this.notifySoundPath,
at: pickedDate,
every: 'minute'
};
this.localNotifications.schedule(options)
NO WORK T_T
let options = {
id: this.idGenerator.generate(),
text: this.timeToMeditateNotificationText,
sound: this.notifySoundPath,
at: pickedDate,
every: 'day'
};
this.localNotifications.schedule(options)
When every param has 'day' value doesn't work, but if I put 'minute' value works :|
@JoueBien and @mschristo , I was having the same problem here, so I changed "count" for 30, the multiple notifications didn't happen and it repeated for the next 2 days (Didn't test more). Probably will repeat for the next 30 days. One trick could be rescheduling the notification every time the user opens the app. Actually this is a nice feature to not repeat infinitely if the app is not being used. 🗡
I had the same problem and that works for me. I use "count: 365"
Any update? I have the same issue. I need to notifiy every day at 08:00 AM and in Android it alert several times in the 08:00:XX minute.
this.localNotifications.schedule({
title: 'XXXXXXXXXXX',
text: 'YYYYYYYYYYYY',
icon: 'assets/img/icon.png',
vibrate: true,
trigger: { every: { hour: 8, minute: 0 }}
});
Like i need to be notified every day, i can't set the "count" atribute to 1.
Any workaround?
Thanks!
I believe this is still broken 3 years later. I changed companies since then and they don't use ionic, so I haven't bothered to follow up.
Considering that this package is so widely used by ionic projects you'd think someone would have fixed the issue by now.
Back when we ran into this issue we set up a week's worth of notifications every time the user opened the app.
I believe this is still broken 3 years later. I changed companies since then and they don't use ionic, so I haven't bothered to follow up.
Considering that this package is so widely used by ionic projects you'd think someone would have fixed the issue by now.
Back when we ran into this issue we set up a week's worth of notifications every time the user opened the app.
Thank for your comment. What you say, is to set the "count" atribute to 7 (in my case for 1 notification a day) no?.
I have a question about that. Now, when I start the app I schedule this notifications, and if I close de app, at 08:00 the notification is fired (well done). If I set a week's worth of notifications it could be that the notifications will be fired so many times as count*(number of app opened times)?
Thank you!
Any update? I have the same issue. I need to notifiy every day at 08:00 AM and in Android it alert several times in the 08:00:XX minute.
this.localNotifications.schedule({ title: 'XXXXXXXXXXX', text: 'YYYYYYYYYYYY', icon: 'assets/img/icon.png', vibrate: true, trigger: { every: { hour: 8, minute: 0 }} });
Like i need to be notified every day, i can't set the "count" atribute to 1.
Any workaround?
Thanks!
Try to use "count: 365" and execute "schedule()" each time the app is used. Only users who don't use the app during 1 year stop receiving notifications. This solves this issue (multiple notifications).
at 08:00 the notification is fired (well done). If I set a week's worth of notifications it could be that the notifications will be fired so many times as count*(number of app opened times)?
Thank you
We ditched the repeating and just set up a single non-repeating notification for the next seven days in advanced. Every time the user open the app we cleared the 7 notifications and re-set them for the next 7 days after the current day. This worked for us as the app was a spinal recovery app that the users were going to open every day.
@others adding count dose not work (it doesn't get used when every is present) when you have repeats so stop putting it forward as a solution - IT DOSE NOT WORK FOR THAT USE CASE.
Hi guys,
I will use the plugin to schedule weekly notification, so, I set to fire 999 weeks ( same as 19 years ).
count: 999
This was my work around.
I suggest open this folder in your project [ plugins\cordova-plugin-local-notification\src\android\notification\trigger>MatchTrigger.java ] then copy this code `/*
- Apache 2.0 License
- Copyright (c) Sebastian Katzer 2017
- This file contains Original Code and/or Modifications of Original Code
- as defined in and that are subject to the Apache License
- Version 2.0 (the 'License'). You may not use this file except in
- compliance with the License. Please obtain a copy of the License at
- http://opensource.org/licenses/Apache-2.0/ and read it before using this
- file.
- The Original Code and all software distributed under the License are
- distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- Please see the License for the specific language governing rights and
- limitations under the License. */
package de.appplant.cordova.plugin.notification.trigger;
import java.util.Calendar; import java.util.Date; import java.util.List;
import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.DAY; import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.HOUR; import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.MINUTE; import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.MONTH; import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.WEEK; import static de.appplant.cordova.plugin.notification.trigger.DateTrigger.Unit.YEAR; import static java.util.Calendar.DAY_OF_WEEK; import static java.util.Calendar.WEEK_OF_MONTH; import static java.util.Calendar.WEEK_OF_YEAR;
/**
-
Trigger for date matching components. */ public class MatchTrigger extends IntervalTrigger {
// Used to determine the interval private static Unit[] INTERVALS = { null, MINUTE, HOUR, DAY, MONTH, YEAR };
// Maps these crap where Sunday is the 1st day of the week private static int[] WEEKDAYS = { 0, 2, 3, 4, 5, 6, 7, 1 };
// Maps these crap where Sunday is the 1st day of the week private static int[] WEEKDAYS_REV = { 0, 7, 1, 2, 3, 4, 5, 6 };
// The date matching components private final List<Integer> matchers;
// The special matching components private final List<Integer> specials;
private static Unit getUnit(List<Integer> matchers, List<Integer> specials) { Unit unit1 = INTERVALS[1 + matchers.indexOf(null)], unit2 = null;
if (specials.get(0) != null) { unit2 = WEEK; } if (unit2 == null) return unit1; return (unit1.compareTo(unit2) < 0) ? unit2 : unit1;
}
/**
- Date matching trigger from now.
- @param matchers Describes the date matching parts.
-
{ day: 15, month: ... }
- @param specials Describes the date matching parts.
-
{ weekday: 1, weekOfMonth: ... }
*/ public MatchTrigger(List<Integer> matchers, List<Integer> specials) { super(1, getUnit(matchers, specials));
if (specials.get(0) != null) { specials.set(0, WEEKDAYS[specials.get(0)]); } this.matchers = matchers; this.specials = specials;
}
/**
-
Gets the date from where to start calculating the initial trigger date. */ private Calendar getBaseTriggerDate(Date date) { Calendar cal = getCal(date);
cal.set(Calendar.SECOND, 0);
if (matchers.get(0) != null) { cal.set(Calendar.MINUTE, matchers.get(0)); } else { cal.set(Calendar.MINUTE, 0); }
if (matchers.get(1) != null) { cal.set(Calendar.HOUR_OF_DAY, matchers.get(1)); } else { cal.set(Calendar.HOUR_OF_DAY, 0); }
if (matchers.get(2) != null) { cal.set(Calendar.DAY_OF_MONTH, matchers.get(2)); }
if (matchers.get(3) != null) { cal.set(Calendar.MONTH, matchers.get(3) - 1); }
if (matchers.get(4) != null) { cal.set(Calendar.YEAR, matchers.get(4)); }
return cal; }
/**
-
Gets the date when to trigger the notification.
-
@param base The date from where to calculate the trigger date.
-
@return null if there's none trigger date. */ private Date getTriggerDate (Date base) { Calendar cal = getBaseTriggerDate(base); Calendar now = getCal(base);
if (cal.compareTo(now) >= 0) return applySpecials(cal);
if (unit == null || cal.get(Calendar.YEAR) < now.get(Calendar.YEAR)) return null;
if (cal.get(Calendar.MONTH) < now.get(Calendar.MONTH)) { switch (unit) { case MINUTE: case HOUR: case DAY: case WEEK: if (matchers.get(4) == null) { addToDate(cal, now, Calendar.YEAR, 1); break; } else return null; case YEAR: addToDate(cal, now, Calendar.YEAR, 1); break; } } else if (cal.get(Calendar.DAY_OF_YEAR) < now.get(Calendar.DAY_OF_YEAR)) { switch (unit) { case MINUTE: case HOUR: if (matchers.get(3) == null) { addToDate(cal, now, Calendar.MONTH, 1); break; } else if (matchers.get(4) == null) { addToDate(cal, now, Calendar.YEAR, 1); break; } else return null; case MONTH: addToDate(cal, now, Calendar.MONTH, 1); break; case YEAR: addToDate(cal, now, Calendar.YEAR, 1); break; } } else if (cal.get(Calendar.HOUR_OF_DAY) < now.get(Calendar.HOUR_OF_DAY)) { switch (unit) { case MINUTE: if (matchers.get(2) == null) { addToDate(cal, now, Calendar.DAY_OF_YEAR, 1); break; } else if (matchers.get(3) == null) { addToDate(cal, now, Calendar.MONTH, 1); break; } else return null; case HOUR: if (cal.get(Calendar.MINUTE) < now.get(Calendar.MINUTE)) { addToDate(cal, now, Calendar.HOUR_OF_DAY, 1); } else { addToDate(cal, now, Calendar.HOUR_OF_DAY, 0); } break; case DAY: case WEEK: addToDate(cal, now, Calendar.DAY_OF_YEAR, 1); break; case MONTH: addToDate(cal, now, Calendar.MONTH, 1); break; case YEAR: addToDate(cal, now, Calendar.YEAR, 1); break; } } else if (cal.get(Calendar.MINUTE) < now.get(Calendar.MINUTE)) { switch (unit) { case MINUTE: addToDate(cal, now, Calendar.MINUTE, 1); break; case HOUR: addToDate(cal, now, Calendar.HOUR_OF_DAY, 1); break; case DAY: case WEEK: addToDate(cal, now, Calendar.DAY_OF_YEAR, 1); break; case MONTH: addToDate(cal, now, Calendar.MONTH, 1); break; case YEAR: addToDate(cal, now, Calendar.YEAR, 1); break; } }
return applySpecials(cal); }
private Date applySpecials (Calendar cal) { if (specials.get(2) != null && !setWeekOfMonth(cal)) return null;
if (specials.get(0) != null && !setDayOfWeek(cal)) return null; return cal.getTime();
}
/**
-
Gets the next trigger date.
-
@param base The date from where to calculate the trigger date.
-
@return null if there's none next trigger date. */ @Override public Date getNextTriggerDate (Date base) { Date date = base;
if (getOccurrence() > 1) { Calendar cal = getCal(base); addInterval(cal); date = cal.getTime(); }
incOccurrence();
return getTriggerDate(date); }
/**
- Sets the field value of now to date and adds by count. */ private void addToDate (Calendar cal, Calendar now, int field, int count) { cal.set(field, now.get(field)); cal.add(field, count); }
/**
-
Set the day of the year but ensure that the calendar does point to a
-
date in future.
-
@param cal The calendar to manipulate.
-
@return true if the operation could be made. */ private boolean setDayOfWeek (Calendar cal) { cal.setFirstDayOfWeek(Calendar.MONDAY); int day = WEEKDAYS_REV[cal.get(DAY_OF_WEEK)]; int month = cal.get(Calendar.MONTH); int year = cal.get(Calendar.YEAR); int dayToSet = WEEKDAYS_REV[specials.get(0)];
if (matchers.get(2) != null) return false;
if (day > dayToSet) { if (specials.get(2) == null) { cal.add(WEEK_OF_YEAR, 1); } else if (matchers.get(3) == null) { cal.add(Calendar.MONTH, 1); } else if (matchers.get(4) == null) { cal.add(Calendar.YEAR, 1); } else return false; }
cal.set(Calendar.SECOND, 0); cal.set(DAY_OF_WEEK, specials.get(0));
if (matchers.get(3) != null && cal.get(Calendar.MONTH) != month) return false;
//noinspection RedundantIfStatement if (matchers.get(4) != null && cal.get(Calendar.YEAR) != year) return false;
return true; }
/**
-
Set the week of the month but ensure that the calendar does point to a
-
date in future.
-
@param cal The calendar to manipulate.
-
@return true if the operation could be made. */ private boolean setWeekOfMonth (Calendar cal) { int week = cal.get(WEEK_OF_MONTH); int year = cal.get(Calendar.YEAR); int weekToSet = specials.get(2);
if (week > weekToSet) { if (matchers.get(3) == null) { cal.add(Calendar.MONTH, 1); } else if (matchers.get(4) == null) { cal.add(Calendar.YEAR, 1); } else return false;
if (matchers.get(4) != null && cal.get(Calendar.YEAR) != year) return false;
}
int month = cal.get(Calendar.MONTH);
cal.set(WEEK_OF_MONTH, weekToSet);
if (cal.get(Calendar.MONTH) != month) { cal.set(Calendar.DAY_OF_MONTH, 1); cal.set(Calendar.MONTH, month); } else if (matchers.get(2) == null && week != weekToSet) { cal.set(DAY_OF_WEEK, 2); }
return true; } }` and replace the existing code with this in MatchTigger.java file.