cordova-plugin-local-notifications: Notifications only works if being triggered at the current time, not a time in future!

WARNING: IF YOU IGNORE THIS TEMPLATE, WE’LL IGNORE YOUR ISSUE. YOU MUST FILL THIS IN!

Last year, I have used this plugin for an Ionic v3 application (which I no longer have the access to the source code so no idea which version it was) and it worked just great. However, now when I tried to used it in another new one, it doesn’t operate as it should. In short, I cannot make local notification comes at a specific time in the future, only at the current moment, there is no error log shown when calling the plugin.

Any help would be much appreciated.

Your Environment

Plugin: cordova-plugin-local-notification 0.9.0-beta.2 “LocalNotification”

Ionic: Ionic CLI : 5.2.5 (C:\Users\hoangN\AppData\Roaming\npm\node_modules\ionic) Ionic Framework : ionic-angular 3.9.2 @ionic/app-scripts : 3.1.8

Cordova: Cordova CLI : 8.1.2 (cordova-lib@8.1.1) Cordova Platforms : android 8.1.0 Cordova Plugins : cordova-plugin-ionic-keyboard 2.0.5, cordova-plugin-ionic-webview 1.1.1, (and 13 other plugins)

Utility: cordova-res : 0.8.0 (update available: 0.8.1) native-run : 0.2.9

System: NodeJS : v10.16.3 (C:\Program Files\nodejs\node.exe) npm : 6.9.0 OS : Windows 10

Expected Behavior

Notifications should come at the time specified in the trigger param.

Actual Behavior

Notifications only come at current moment, no future triggering.

Steps to Reproduce

This is the simplified piece of code that I’m using in order to demonstarte this issue, I’ve created 2 button to fire notifications, one is for current moment, one is for 2 minutes later from that.

 setLocalNotiNow() {
    let promise = new Promise(() => {
      let currdate = moment().format('YYYY-MM-DD HH:mm:ss');  
      let currentMoment= moment(currdate,'YYYY-MM-DD HH:mm:ss' )
      console.log(currentMoment.toDate())
      this.localNotifications.schedule({
        text: 'Xin vui lòng đến đúng lịch để chúng tôi có thể phục vụ bạn tốt hơn.',
        trigger: { at: currentMoment.toDate() },
        led: 'FF0000',
        sound: null,
        priority:1,
        foreground:true
      });

    });
    return promise;
  }
  setLocalNotiLater() {
    let promise = new Promise(() => {
      let currdate = moment().format('YYYY-MM-DD HH:mm:ss');  
      let currentMoment= moment(currdate,'YYYY-MM-DD HH:mm:ss' )
      let time = moment.duration("00:02:00");
      currentMoment.add(time)
      console.log(currentMoment.toDate())
      this.localNotifications.schedule({
        text: 'Xin vui lòng đến đúng lịch để chúng tôi có thể phục vụ bạn tốt hơn.',
        trigger: { at: currentMoment.toDate() },
        led: 'FF0000',
        sound: null,
        priority:1,
        foreground:true
      });

    });
    return promise;
  }

Context

What were you trying to do?

Debug logs

Include iOS / Android logs

  • ios XCode logs
  • Android: $ adb logcat

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Reactions: 1
  • Comments: 19

Most upvoted comments

I have had the same issue. Actually it worked for me few weeks ago and then it stopped working randomly. The notifications worked only when triggered instantly.

But I’ve found a solution! At least for me. The code below was missing on my AndroidManifest.xml. I have no idea why, maybe another plugin messed it up (probably firebasex with his push notifications?)

Anyway just add it to your manifest and it will start working again.

<activity android:exported="false" android:launchMode="singleInstance" android:name="de.appplant.cordova.plugin.localnotification.ClickReceiver" android:theme="@android:style/Theme.Translucent"/>
<provider android:authorities="${applicationId}.provider" android:exported="false" android:grantUriPermissions="true" android:name="de.appplant.cordova.plugin.notification.util.AssetProvider">
  <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/localnotification_provider_paths"/>
</provider>
<receiver android:exported="false" android:name="de.appplant.cordova.plugin.localnotification.TriggerReceiver"/>
<receiver android:exported="false" android:name="de.appplant.cordova.plugin.localnotification.ClearReceiver"/>

I finally found some time to investigate into this, and it seems that my fix is solving this issue. Here is an explanation why it’s not working.

https://github.com/katzer/cordova-plugin-local-notifications/blob/caff55ec758fdf298029ae98aff7f6a8a097feac/src/android/notification/Notification.java#L217-L243

The first line decides if the Notification Intent is triggered immediately or if its a PendingIntent added to the AlarmManager.

The AlarmManager then did not wake up, and fired the notification, because the following Receivers were not added to the AndroidManifest.xml

https://github.com/katzer/cordova-plugin-local-notifications/blob/caff55ec758fdf298029ae98aff7f6a8a097feac/plugin.xml#L103-L135

I fixed this in my fork:

https://github.com/katzer/cordova-plugin-local-notifications/compare/master...m0dch3n:master

So the solution which worked for me is:

cordova plugin remove cordova-plugin-local-notification
cordova plugin add https://github.com/m0dch3n/cordova-plugin-local-notifications.git

It seems this plugin is also no longer maintained, and JFYI, I will neither maintain my fork, unless I find specific problems to my apps.

BTW: I think it has nothing todo with the smallIcon, or array solution, but you can correct me… I suppose it only worked during your debug session, because you stopped long enough in your Breakpoint, so that

!date.after(new Date())

became true…

BTW I changed some other things, mainly that ids don’t need to be integers… As I don’t know all the side effects of this, I don’t suggest to use my repository, but to only merge the mentioned commit into your repository

@Tawpie hahaha, I did have an ah-ha moment following your instructions, confirm that having no foreground attribute and putting notification in array does the magic. Thanks sir.

i’m receiving the notification when adding smallIcon: 'res://favicon'

I actually use a private fork of beta3 that includes fixes for 1664 and 1541. Scheduling new Date(Date.now().getTime + 1000) does work for me on Oreo (Pixel 1)—I think the notification is late by a few seconds but it does fire. I don’t use foreground (don’t like the UX interrupted) so the notification dings the phone and appears in the notification tray. You might try without foreground?

Besides me not using Ionic (I use just Cordova), the only difference in the way I schedule is I put the notifications into an array even if there is just one notification to schedule. I found early on that scheduling multiple notifications one at a time could step on each other unless you allowed a long time between scheduling activities (or wait for .on confirmation) to allow the phone to do its thing before you start scheduling another. The extra logic wasn’t worth it, arrays work every time.

I doubt you’ll have an ah-ha moment but this code does work on 9 (my 10 device is with my testers so I’m stuck with 9!!)

/**
 * an_scheduleSingleLocalNotification
 *
 * queue up a system local notification to fire at a specific time. when dAtDate is blank
 * or bogus we'll go for 10 seconds from now.
 *
 * @param sActions {string} // the 'type' of action, input or button or...
 * @param sTitle {string} // title string for the notification
 * @param sText {string} // body text for the notification
 * @param dAtDate {object} // date object containing the fire time
 * @param bWithoutCancelling {boolean} // when true will NOT cancel existing notifications first
 */
export function an_scheduleSingleLocalNotification(
  sActions,
  sTitle,
  sText,
  dAtDate,
  bWithoutCancelling
) {
  if (an_isLocalNotificationPluginValid()) {
    if (typeof bWithoutCancelling === "undefined") {
      bWithoutCancelling = true;
    }

    // compute a fire date and time
    let dFireAt = new Date(dAtDate);
    if (
      typeof dFireAt === "undefined" ||
      dFireAt.toString() === "Invalid Date"
    ) {
      dFireAt = dateAdd(new Date(), 10, "secs");
    }

    // build the reminder, start with a basic no-action reminder
    let oKatzerLNPluginReminder = {
      id: new Date().getTime(),
      trigger: { at: new Date(dFireAt) }, // MUST be a date object
      text:
        sText ||
        "This is a test message scheduled to fire at " + dFireAt.toString(),
      title: sTitle || "TESTING!",
      data:
        '{"rcls":"' +
        applicationStateStore.getState().activeModule +
        '","nid":"Test"}'
    };

    // add actions as indicated
    switch (sActions) {
      case SPGaD.ksLocalNotificationCategoryID_buttons_yes_no:
        oKatzerLNPluginReminder.actions = SPGaD.ksLocalNotificationCategoryID_buttons_yes_no;

        // install the listeners
        cordova.plugins.notification.local.un(
          SPGaD.ksLocalNotificationActionID_buttons_yes_no_ButtonID_yes,
          handleLNResponse_yes
        );
        cordova.plugins.notification.local.on(
          SPGaD.ksLocalNotificationActionID_buttons_yes_no_ButtonID_yes,
          handleLNResponse_yes
        );

        cordova.plugins.notification.local.un(
          SPGaD.ksLocalNotificationActionID_buttons_yes_no_ButtonID_no,
          handleLNResponse_no
        );
        cordova.plugins.notification.local.on(
          SPGaD.ksLocalNotificationActionID_buttons_yes_no_ButtonID_no,
          handleLNResponse_no
        );
        break;
      case SPGaD.ksLocalNotificationActionID_input:
        oKatzerLNPluginReminder.actions = SPGaD.ksLocalNotificationCategoryID_input;

        cordova.plugins.notification.local.un(
          SPGaD.ksLocalNotificationActionID_input,
          handleLNResponse_input
        );
        cordova.plugins.notification.local.on(
          SPGaD.ksLocalNotificationActionID_input,
          handleLNResponse_input
        );
        break;
      default:
        break;
    }

    // android can show the smallIcon in the actionbar
    if (gsRunningOnPlatform .toLowerCase() === kPlatformandroid ) {
      oKatzerLNPluginReminder.smallIcon = "res://" + SPGaD.gsANAndroidPushIcon;
    }

    let aMultiRemindersForKatzerLNPlugin = [oKatzerLNPluginReminder];

    if (aMultiRemindersForKatzerLNPlugin.length) {
      pRunPromisesSerially(getTestKatzerActionGroupArray()).then(
        () => {
          if (!bWithoutCancelling) {
            cordova.plugins.notification.local.cancelAll(
              function scheduleAllRemindersViaKatzer() {
                console.log(
                  "ANSLMH.an_sSLNFR - cancellation completed. Elapsed: " +
                  (new Date().getTime() - new Date().getTime()) / 1000 +
                  " secs"
                );
                console.log(
                  " ANSLMH.an_sSLNFR - scheduling >" +
                  aMultiRemindersForKatzerLNPlugin.length +
                  "< local reminders"
                );
                setLSInt(SPGaD.ksLocalMessagesActuallyScheduled, 0); // reset the count
                SPGaD.goScheduledLocalMessages = {}; // purge our copy

                cordova.plugins.notification.local.schedule(
                  aMultiRemindersForKatzerLNPlugin
                );
              }
            );
          } else {
            console.log(
              " ANSLMH.an_sSLNFR - scheduling >" +
              aMultiRemindersForKatzerLNPlugin.length +
              "< local reminders without cancelling"
            );

            cordova.plugins.notification.local.schedule(
              aMultiRemindersForKatzerLNPlugin
            );
          }
        },
        () => {
          console.error(
            "ANSLMH.an_sSLNFR - Can't schedule local notifications, the actions group promise was rejected"
          );
        }
      );
    } else {
      console.error(
        "ANSLMH.an_sSLNFR - Can't schedule local notifications, the local notification plugin is invalid"
      );
    }
  }
}