Step 3: Add Alarms and Notifications

In this step, you will learn:

  • How to wake your app at specified intervals to execute background tasks.
  • How to use on-screen notifications to draw attention to something important.

Estimated time to complete this step: 20 minutes. To preview what you will complete in this step, jump down to the bottom of this page ↓.

Enhance your Todo app with reminders

Enhance the Todo app by adding functionality to remind the user if they have open todos—even when the app is closed.

First, you need to add a way for the app to regularly check for uncompleted todos. Next, the app needs to display a message to the user, even if the Todo app window is closed. To accomplish this, you need to understand how alarms and notifications work in Chrome Apps.

Add alarms

Use chrome.alarms to set a wake up interval. As long as Chrome is running, the alarm listener is called at approximately the interval set.

Update app permissions

In manifest.json, request the "alarms" permission:

"permissions": ["storage", "alarms"],

Update background scripts

In background.js, add an onAlarm listener. For now, the callback function will just log a message to the Console whenever there is a todo item:

chrome.app.runtime.onLaunched.addListener(function() {
  chrome.app.window.create('index.html', {
    id: 'main',
    bounds: { width: 620, height: 500 }
  });
});
chrome.alarms.onAlarm.addListener(function( alarm ) {
  console.log("Got an alarm!", alarm);
});

Update HTML view

In index.html, add an Activate alarm button:

<footer id="info">
  <button id="toggleAlarm">Activate alarm</button>
  ...
</footer>

You now need to write the JavaScript event handler for this new button. Recall from Step 2 that one of the most common CSP non-compliances is caused by inline JavaScript. In index.html, add this line to import a new alarms.js file which you will create in the following step:

  </footer>
  ...
  <script src="js/app.js"></script>
  <script src="js/alarms.js"></script>
</body>

Create alarms script

Create a new file in your js folder called alarms.js.

Use the code below to add checkAlarm(), createAlarm(), cancelAlarm() and toggleAlarm() methods, along with a click event handler to toggle the alarm when the Activate alarm button is clicked.

(function () {
  'use strict';
   var alarmName = 'remindme';
   function checkAlarm(callback) {
     chrome.alarms.getAll(function(alarms) {
       var hasAlarm = alarms.some(function(a) {
         return a.name == alarmName;
       });
       var newLabel;
       if (hasAlarm) {
         newLabel = 'Cancel alarm';
       } else {
         newLabel = 'Activate alarm';
       }
       document.getElementById('toggleAlarm').innerText = newLabel;
       if (callback) callback(hasAlarm);
     })
   }
   function createAlarm() {
     chrome.alarms.create(alarmName, {
       delayInMinutes: 0.1, periodInMinutes: 0.1});
   }
   function cancelAlarm() {
     chrome.alarms.clear(alarmName);
   }
   function doToggleAlarm() {
     checkAlarm( function(hasAlarm) {
       if (hasAlarm) {
         cancelAlarm();
       } else {
         createAlarm();
       }
       checkAlarm();
     });
   }
  $$('#toggleAlarm').addEventListener('click', doToggleAlarm);
  checkAlarm();
})();

Reload your app and spend a few moments activating (and deactivating) the alarm.

Inspect background page messages

Whenever you have the alarm activated, you should see log messages being printed in the Console every time the alarm "rings":

Alarm messages printing to the Console

You should notice that:

  • Even when you close the Todo app window, the alarms will keep coming.
  • On platforms other than ChromeOS, if you completely close all Chrome browser instances, alarms won't trigger.

Let's go over some of the pieces in alarms.js that use chrome.alarms methods one by one.

Create alarms

In createAlarm(), use the chrome.alarms.create() API to create an alarm when Activate alarm is toggled.

chrome.alarms.create(alarmName, {delayInMinutes: 0.1, periodInMinutes: 0.1});

The first parameter is an optional string identifying an unique name for your alarm, for example, remindme. (Note: You need to set an alarm name in order to cancel it by name.)

The second parameter is an alarmInfo object. Valid properties for alarmInfo include when or delayInMinutes, and periodInMinutes. In order to reduce the load on the user's machine, Chrome limits alarms to once every minute. We are using small values (0.1 of a minute) here for demo purposes only.

Clear alarms

In cancelAlarm(), use the chrome.alarms.clear() API to cancel an alarm when Cancel alarm is toggled.

chrome.alarms.clear(alarmName);

The first parameter should be the identifying string you used as an alarm name in chrome.alarms.create().

The second (optional) parameter is a callback function that should take on the format:

function(boolean wasCleared) {...};

Get alarms

In checkAlarm(), use the chrome.alarms.getAll() API to get an array of all created alarms in order to update the UI state of the toggle button.

getAll() accepts a callback function that passes in an array of Alarm objects. To see what's in an Alarm, you can inspect running alarms in the DevTools Console like so:

chrome.alarms.getAll(function(alarms) {
  console.log(alarms);
  console.log(alarms[0]);
});

This will output an object such as {name: "remindme", periodInMinutes: 0.1, scheduledTime: 1397587981166.858} as seen below:

Use getAll() to inspect running alarms.

Get ready for the next section

Now that alarms are in place to poll the app at regular intervals, use this as a base for adding visual notifications.

Add notifications

Let's change the alarm notification to something the user can easily notice. Use chrome.notifications to show a desktop notification like the one below:

Our Todo app notification

When the user clicks on the notification, the Todo app window should come into view.

Update app permissions

In manifest.json, request the "notifications" permission:

"permissions": ["storage", "alarms", "notifications"],

Update background scripts

In background.js, refactor the chrome.app.window.create() callback into a standalone method so you can reuse it:

chrome.app.runtime.onLaunched.addListener(function() {
function launch() {
  chrome.app.window.create('index.html', {
    id: 'main',
    bounds: { width: 620, height: 500 }
  });
}
});
chrome.app.runtime.onLaunched.addListener(launch);
...

Update alarm listener

At the top of the background.js, add a variable for a database name that's used in the alarm listener:

var dbName = 'todos-vanillajs';

The value of dbName is the same database name set in line 17 of js/app.js:

var todo = new Todo('todos-vanillajs');

Create a notification

Instead of simply logging a new alarm to the Console, update the onAlarm listener to get stored data via chrome.storage.local.get() and call a showNotification() method:

chrome.alarms.onAlarm.addListener(function( alarm ) {
  console.log("Got an alarm!", alarm);
  chrome.storage.local.get(dbName, showNotification);
});

Add this showNotification() method to background.js:

function launch(){
  ...
}

function showNotification(storedData) {
  var openTodos = 0;
  if ( storedData[dbName].todos ) {
    storedData[dbName].todos.forEach(function(todo) {
      if ( !todo.completed ) {
        openTodos++;
      }
    });
  }
  if (openTodos>0) {
    // Now create the notification
    chrome.notifications.create('reminder', {
        type: 'basic',
        iconUrl: 'icon_128.png',
        title: 'Don\'t forget!',
        message: 'You have '+openTodos+' things to do. Wake up, dude!'
     }, function(notificationId) {});
  }
}

chrome.app.runtime.onLaunched.addListener(launch);
...

showNotification() will check for open (uncompleted) todo items. If there is at least one open todo item, create a notification popup via chrome.notifications.create().

The first parameter is an uniquely identifying notification name. You must have an id set in order to clear or handle interactions with that particular notification. If the id matches an existing notification, create() first clears that notification before making a new notification.

The second parameter is a NotificationOptions object. There are many options for rendering the notification popup. Here we are using a "basic" notification with an icon, title, and message. Other notification types include images, lists, and progress indicators. Feel free to return to this section when you are done Step 3 and experiment with other notification features.

The third (optional) parameter is a callback method that should take on the format:

function(string notificationId) {...};

Handle notification interactions

Open the Todo app when the user clicks on the notification. At the end of background.js, create a chrome.notifications.onClicked event handler:

chrome.notifications.onClicked.addListener(function() {
  launch();
});

The event handler callback simply calls the launch() method. chrome.app.window.create() either creates a new Chrome App window if one doesn't already exist, or brings into focus the currently open window that has the window id of main.

Launch your finished Todo app

You are done Step 3! Reload your Todo app now with reminders.

Check these behaviors work as expected:

  • If you don't have any uncompleted todo items, there are no popup notifications.
  • If you click on the notification when your app is closed, the Todo app will open or come into focus.

Troubleshooting

Your final background.js file should look like this. If notifications are not showing up, confirm that your Chrome is version 28 or higher. If notifications still don't show up, check for error messages in the DevTools Console on both the main window (right click > Inspect Element) and the background page (right click > Inspect Background Page).

For more information

For more detailed information about some of the APIs introduced in this step, refer to:

Ready to continue onto the next step? Go to Step 4 - Open external links with a webview »