Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / common / extensions / docs / templates / articles / app_codelab_alarms.html
blob75752d8b33a1531f5cf666af9bb3fa153729ac95
1 <h1 id="add-alarms-notifications">
2 <span class="h1-step">Step 3:</span>
3 Add Alarms and Notifications
4 </h1>
6 <p class="note">
7 <strong>Want to start fresh from here?</strong>
8 Find the previous step's code in the <a href="https://github.com/mangini/io13-codelab/archive/master.zip">reference code zip</a> under <strong><em>cheat_code > solution_for_step2</strong></em>.
9 </p>
11 <p>In this step, you will learn:</p>
13 <ul>
14 <li>How to wake your app at specified intervals to execute background tasks.</li>
15 <li>How to use on-screen notifications to draw attention to something important.</li>
16 </ul>
18 <p>
19 <em>Estimated time to complete this step: 20 minutes.</em>
20 <br>
21 To preview what you will complete in this step, <a href="#launch">jump down to the bottom of this page &#8595;</a>.
22 </p>
24 <h2 id="overview">Enhance your Todo app with reminders</h2>
26 <p>Enhance the Todo app by adding functionality to remind the user if they have open todos &mdash; even when the app is closed.</p>
28 <p>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.</p>
30 <h2 id="alarms">Add alarms</h2>
32 <p>Use <a href="/apps/alarms.html"><code>chrome.alarms</code></a>
33 to set a wake up interval. As long as Chrome is running, the alarm listener is called at
34 approximately the interval set.</p>
36 <h3 id="update-permissions-alarms">Update app permissions</h3>
38 <p>In <strong><em>manifest.json</em></strong>, request the <code>"alarms"</code> permission:</p>
40 <pre data-filename="manifest.json">
41 "permissions": ["storage"<b>, "alarms"</b>],
42 </pre>
44 <h3 id="update-background-script-alarms">Update background scripts</h3>
46 <p>In <strong><em>background.js</em></strong>, add an <a href="/apps/alarms#event-onAlarm"><code>onAlarm</code></a> listener.
47 For now, the callback function will just log a message to the Console whenever there is a todo item:</p>
49 <pre data-filename="background.js">
50 chrome.app.runtime.onLaunched.addListener(function() {
51 chrome.app.window.create('index.html', {
52 id: 'main',
53 bounds: { width: 620, height: 500 }
54 });
55 });
56 <b>chrome.alarms.onAlarm.addListener(function( alarm ) {
57 console.log("Got an alarm!", alarm);
58 });</b>
59 </pre>
61 <h3 id="update-html-view-alarms">Update HTML view</h3>
63 <p>In <strong><em>index.html</em></strong>, add an <strong>Activate alarm</strong> button:</p>
65 <pre data-filename="index.html">
66 &lt;footer id="info"&gt;
67 <b>&lt;button id="toggleAlarm"&gt;Activate alarm&lt;/button&gt;</b>
68 ...
69 &lt;/footer&gt;
70 </pre>
72 <p>You now need to write the JavaScript event handler for this new button.
73 Recall from <a href="/apps/app_codelab_import_todomvc#csp-compliance">Step 2</a>
74 that one of the most common CSP non-compliances is caused by inline JavaScript.
75 In <em>index.html</em>, add this line to import a new <em>alarms.js</em>
76 file which you will create in the following step:</p>
78 <pre data-filename="index.html">
79 &lt;/footer&gt;
80 ...
81 &lt;script src="js/app.js"&gt;&lt;/script&gt;
82 <b>&lt;script src="js/alarms.js"&gt;&lt;/script&gt;</b>
83 &lt;/body&gt;
84 </pre>
86 <h3 id="add-alarms-script">Create alarms script</h3>
88 <p>Create a new file in your <strong><em>js</em></strong> folder called <strong><em>alarms.js</em></strong>.</p>
90 <p>Use the code below to add <code>checkAlarm()</code>, <code>createAlarm()</code>,
91 <code>cancelAlarm()</code> and <code>toggleAlarm()</code> methods, along with a
92 click event handler to toggle the
93 alarm when the <strong>Activate alarm</strong> button is clicked.</p>
95 <pre data-filename="alarms.js">
96 (function () {
97 'use strict';
98 var alarmName = 'remindme';
99 function checkAlarm(callback) {
100 chrome.alarms.getAll(function(alarms) {
101 var hasAlarm = alarms.some(function(a) {
102 return a.name == alarmName;
104 var newLabel;
105 if (hasAlarm) {
106 newLabel = 'Cancel alarm';
107 } else {
108 newLabel = 'Activate alarm';
110 document.getElementById('toggleAlarm').innerText = newLabel;
111 if (callback) callback(hasAlarm);
114 function createAlarm() {
115 chrome.alarms.create(alarmName, {
116 delayInMinutes: 0.1, periodInMinutes: 0.1});
118 function cancelAlarm() {
119 chrome.alarms.clear(alarmName);
121 function doToggleAlarm() {
122 checkAlarm( function(hasAlarm) {
123 if (hasAlarm) {
124 cancelAlarm();
125 } else {
126 createAlarm();
128 checkAlarm();
131 $$('#toggleAlarm').addEventListener('click', doToggleAlarm);
132 checkAlarm();
133 })();
134 </pre>
136 <p>Reload your app and spend a few moments activating (and deactivating) the alarm.</p>
138 <p class="note">Since the log messages are being sent to the Console via the event page (aka background script),
139 you need to <strong>right-click > Inspect Background Page</strong> to see the log messages:</p>
141 <figure>
142 <img src="{{static}}/images/app_codelab/inspect-background-page.png" alt="Inspect background page messages"/>
143 </figure>
145 <p class="note">It's also worthwhile to continue to use <strong>right-click > Inspect Element</strong>
146 to make sure you do not have errors in other JavaScript files.</p>
148 <p>Whenever you have the alarm activated, you should see log messages being printed
149 in the Console every time the alarm "rings":</p>
151 <figure>
152 <img src="{{static}}/images/app_codelab/alarm-console-logs.png" alt="Alarm messages printing to the Console"/>
153 </figure>
155 <p>You should notice that:</p>
157 <ul>
158 <li>Even when you close the Todo app window, the alarms will keep coming.</li>
159 <li>On platforms other than Chrome OS, if you completely close all Chrome browser instances, alarms won't trigger.</li>
160 </ul>
162 <p>Let's go over some of the pieces in <em>alarms.js</em> that use <code>chrome.alarms</code> methods one by one.</p>
164 <h3 id="create-alarms">Create alarms</h3>
166 <p>In <code>createAlarm()</code>, use the <a href="/apps/alarms#method-create"><code>chrome.alarms.create()</code></a> API to create an alarm when <strong>Activate alarm</strong> is toggled.</p>
168 <pre data-filename="alarms.js">
169 chrome.alarms.create(alarmName, {delayInMinutes: 0.1, periodInMinutes: 0.1});
170 </pre>
172 <p>The first parameter is an optional string identifying an unique name for your alarm,
173 for example, <code>remindme</code>.
174 (Note: You need to set an alarm name in order to cancel it by name.)</p>
176 <p>The second parameter is an <code>alarmInfo</code> object. Valid properties for <code>alarmInfo</code> include <code>when</code> or <code>delayInMinutes</code>, and <code>periodInMinutes</code>. In order to reduce the load on the user's machine, Chrome limits alarms to once every minute.
177 We are using small values (0.1 of a minute) here for demo purposes only.</p>
179 <h3 id="clear-alarms">Clear alarms</h3>
181 <p>In <code>cancelAlarm()</code>, use the <a href="/apps/alarms#method-clear"><code>chrome.alarms.clear()</code></a> API to cancel an alarm when <strong>Cancel alarm</strong> is toggled.</p>
183 <pre data-filename="alarms.js">
184 chrome.alarms.clear(alarmName);
185 </pre>
187 <p>The first parameter should be the identifying string you used as an alarm name in <code>chrome.alarms.create()</code>.</p>
189 <p>The second (optional) parameter is a callback function that should take on the format:</p>
191 <pre>function(boolean wasCleared) {...};</pre>
193 <h3 id="get-alarms">Get alarms</h3>
195 <p>In <code>checkAlarm()</code>, use the <a href="/apps/alarms#method-getAll"><code>chrome.alarms.getAll()</code></a> API to get an array of all created alarms in order to update the UI state of the toggle button.</p>
197 <p><code>getAll()</code> accepts a callback function that passes in an array of <code>Alarm</code> objects. To see what's in an <code>Alarm</code>, you can inspect running alarms in the DevTools Console like so:</p>
199 <pre>
200 chrome.alarms.getAll(function(alarms) {
201 console.log(alarms);
202 console.log(alarms[0]);
204 </pre>
206 <p>This will output an object such as <code>{name: "remindme", periodInMinutes: 0.1, scheduledTime: 1397587981166.858}</code> as seen below:</p>
208 <figure>
209 <img src="{{static}}/images/app_codelab/alarms-getAll-console.png" alt="Use getAll() to inspect running alarms."/>
210 </figure>
212 <h3 id="next-section">Get ready for the next section</h3>
214 <p>Now that alarms are in place to poll the app at regular intervals, use this as a base for adding visual notifications.</p>
216 <h2 id="notifications">Add notifications</h2>
218 <p>Let's change the alarm notification to something the user can easily notice.
219 Use <a href="/apps/notifications"><code>chrome.notifications</code></a>
220 to show a desktop notification like the one below:</p>
222 <figure>
223 <img src="{{static}}/images/app_codelab/notification-example.png" alt="Our Todo app notification"/>
224 </figure>
226 <p>When the user clicks on the notification, the Todo app window should come into view.</p>
228 <h3 id="update-permissions-notifications">Update app permissions</h3>
230 <p>In <strong><em>manifest.json</em></strong>, request the <code>"notifications"</code> permission:</p>
232 <pre data-filename="manifest.json">
233 "permissions": ["storage", "alarms"<b>, "notifications"</b>],
234 </pre>
236 <h3 id="update-background-script-notifications">Update background scripts</h3>
238 <p>In <strong><em>background.js</em></strong>, refactor the <code>chrome.app.window.create()</code> callback into a standalone method so you can reuse it:</p>
240 <pre data-filename="background.js">
241 <strike>chrome.app.runtime.onLaunched.addListener(function() {</strike>
242 <b>function launch() {</b>
243 chrome.app.window.create('index.html', {
244 id: 'main',
245 bounds: { width: 620, height: 500 }
247 <b>}</b>
248 <strike>});</strike>
249 <b>chrome.app.runtime.onLaunched.addListener(launch);</b>
251 </pre>
253 <h3 id="update-alarm-listener">Update alarm listener</h3>
255 <p>At the top of the <em>background.js</em>, add a variable for a database name that's used in the alarm listener:</p>
257 <pre data-filename="background.js">
258 <b>var dbName = 'todos-vanillajs';</b>
259 </pre>
261 <p>The value of <code>dbName</code> is the same database name set in line 17 of <em>js/app.js</em>:</p>
263 <pre data-filename="app.js">
264 var todo = new Todo('todos-vanillajs');
265 </pre>
267 <h3 id="create-notification">Create a notification</h3>
269 <p>Instead of simply logging a new alarm to the Console, update the <code>onAlarm</code> listener
270 to get stored data via <code>chrome.storage.local.get()</code> and
271 call a <code>showNotification()</code> method:</p>
273 <pre data-filename="background.js">
274 chrome.alarms.onAlarm.addListener(function( alarm ) {
275 <strike>console.log("Got an alarm!", alarm);</strike>
276 <b>chrome.storage.local.get(dbName, showNotification);</b>
278 </pre>
280 <p>Add this <code>showNotification()</code> method to <em>background.js</em>:</p>
282 <pre data-filename="background.js">
283 function launch(){
287 <b>function showNotification(storedData) {
288 var openTodos = 0;
289 if ( storedData[dbName].todos ) {
290 storedData[dbName].todos.forEach(function(todo) {
291 if ( !todo.completed ) {
292 openTodos++;
296 if (openTodos>0) {
297 // Now create the notification
298 chrome.notifications.create('reminder', {
299 type: 'basic',
300 iconUrl: 'icon_128.png',
301 title: 'Don\'t forget!',
302 message: 'You have '+openTodos+' things to do. Wake up, dude!'
303 }, function(notificationId) {});
305 }</b>
307 chrome.app.runtime.onLaunched.addListener(launch);
309 </pre>
311 <p><code>showNotification()</code> will check for open (uncompleted) todo items. If there is at least one open todo item, create a notification popup via <a href="/apps/notifications#method-create"><code>chrome.notifications.create()</code></a>.</p>
313 <p>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, <code>create()</code> first clears that notification before making a new notification.</p>
315 <p>The second parameter is a <a href="/apps/notifications#type-NotificationOptions"><code>NotificationOptions</code></a> 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.</p>
317 <p>The third (optional) parameter is a callback method that should take on the format:</p>
319 <pre>
320 function(string notificationId) {...};
321 </pre>
323 <h3 id="interact-with-notification">Handle notification interactions</h3>
325 <p>Open the Todo app when the user clicks on the notification. At the end of <em>background.js</em>, create a <a href="/apps/notifications#event-onClicked"><code>chrome.notifications.onClicked</code></a> event handler:</p>
327 <pre data-filename="background.js">
328 <b>chrome.notifications.onClicked.addListener(function() {
329 launch();
330 });</b>
331 </pre>
333 <p>The event handler callback simply calls the <code>launch()</code> method.
334 <code>chrome.app.window.create()</code> either creates a new Chrome App window
335 if one doesn't already exist,
336 or brings into focus the currently open window
337 that has the window id of <code>main</code>.</p>
339 <h2 id="launch">Launch your finished Todo app</h2>
341 <p>You are done Step 3! Reload your Todo app now with reminders.</p>
343 <figure>
344 <img src="{{static}}/images/app_codelab/step3-completed.gif" alt="The Todo app with notifications"/>
345 </figure>
347 <p>Check these behaviors work as expected:</p>
349 <ul>
350 <li>If you don't have any uncompleted todo items, there are no popup notifications.</li>
351 <li>If you click on the notification when your app is closed, the Todo app will
352 open or come into focus.</li>
353 </ul>
355 <h3 id="troubleshooting">Troubleshooting</h3>
357 <p>Your final <em>background.js</em> file should look like
358 <a href="https://github.com/mangini/io13-codelab/blob/master/cheat_code/solution_for_step3/background.js">this</a>. If notifications are not showing up, confirm that your Chrome is version 28 or
359 higher. If notifications still don't show up, check for error messages in the
360 DevTools Console on both the main window (<strong>right click > Inspect Element</strong>) and the
361 background page (<strong>right click > Inspect Background Page</strong>).</p>
363 <h2 id="recap">For more information</h2>
365 <p>For more detailed information about some of the APIs introduced in this step, refer to:</p>
367 <ul>
368 <li>
369 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the Chrome developer docs">Declare Permissions</a>
370 <a href="#update-permissions-alarms" class="anchor-link-icon" title="This feature mentioned in 'Update app permissions for alarms'">&#8593;</a>
371 </li>
372 <li>
373 <a href="/apps/alarms.html" title="Read 'chrome.alarms' in the Chrome developer docs">chrome.alarms</a>
374 <a href="#alarms" class="anchor-link-icon" title="This feature mentioned in 'Add alarm reminders'">&#8593;</a>
375 </li>
376 <li>
377 <a href="/apps/alarms#event-onAlarm" title="Read 'chrome.alarms.onAlarm' in the Chrome developer docs">chrome.alarms.onAlarm</a>
378 <a href="#update-background-script-alarms" class="anchor-link-icon" title="This feature mentioned in ''">&#8593;</a>
379 </li>
380 <li>
381 <a href="/apps/alarms#method-create" title="Read 'chrome.alarms.create()' in the Chrome developer docs">chrome.alarms.create()</a>
382 <a href="#create-alarms" class="anchor-link-icon" title="This feature mentioned in 'Create alarms'">&#8593;</a>
383 </li>
384 <li>
385 <a href="/apps/alarms#method-clear" title="Read 'chrome.alarms.clear()' in the Chrome developer docs">chrome.alarms.clear()</a>
386 <a href="#clear-alarms" class="anchor-link-icon" title="This feature mentioned in 'Clear alarms'">&#8593;</a>
387 </li>
388 <li>
389 <a href="/apps/alarms#method-getAll" title="Read 'chrome.alarms.getAll()' in the Chrome developer docs">chrome.alarms.getAll()</a>
390 <a href="#get-alarms" class="anchor-link-icon" title="This feature mentioned in 'Get alarms'">&#8593;</a>
391 </li>
392 <li>
393 <a href="/apps/notifications" title="Read 'chrome.notifications' in the Chrome developer docs">chrome.notifications</a>
394 <a href="#notifications" class="anchor-link-icon" title="This feature mentioned in 'Add notifications'">&#8593;</a>
395 </li>
396 <li>
397 <a href="/apps/notifications#method-create" title="Read 'chrome.notifications.create()' in the Chrome developer docs">chrome.notifications.create()</a>
398 <a href="#create-notification" class="anchor-link-icon" title="This feature mentioned in 'Create a notification'">&#8593;</a>
399 </li>
400 <li>
401 <a href="/apps/notifications#type-NotificationOptions" title="Read 'NotificationOptions' in the Chrome developer docs">NotificationOptions</a>
402 <a href="#create-notification" class="anchor-link-icon" title="This feature mentioned in 'Create a notification'">&#8593;</a>
403 </li>
404 <li>
405 <a href="/apps/notifications#event-onClicked" title="Read 'chrome.notifications.onClicked' in the Chrome developer docs">chrome.notifications.onClicked</a>
406 <a href="#interact-with-notification" class="anchor-link-icon" title="This feature mentioned in 'Handle notification interactions'">&#8593;</a>
407 </li>
408 <!-- <li>
409 <a href="/apps/notifications" title="Read 'chrome.notifications.clear()' in the Chrome developer docs">chrome.notifications.clear()</a>
410 <a href="#clear-notification" class="anchor-link-icon" title="This feature mentioned in 'Clear the notification'">&#8593;</a>
411 </li> -->
412 <!-- <li>
413 <a href="/apps/desktop_notifications" title="Read 'Rich Notifications' in the Chrome developer docs">Rich Notifications</a>
414 <a href="#rich-notifications" class="anchor-link-icon" title="This feature mentioned in 'Learn more about rich notifications'">&#8593;</a>
415 </li> -->
416 </ul>
418 <p>Ready to continue onto the next step? Go to <a href="app_codelab_webview.html">Step 4 - Open external links with a webview &raquo;</a></p>