OLD | NEW |
(Empty) | |
| 1 <h1 id="add-alarms-notifications"> |
| 2 <span class="h1-step">Step 3:</span> |
| 3 Add Alarms and Notifications |
| 4 </h1> |
| 5 |
| 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> |
| 10 |
| 11 <p>In this step, you will learn:</p> |
| 12 |
| 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 importan
t.</li> |
| 16 </ul> |
| 17 |
| 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 ↓</a>. |
| 22 </p> |
| 23 |
| 24 <h2 id="overview">Enhance your Todo app with reminders</h2> |
| 25 |
| 26 <p>Let's enhance our Todo app by adding functionality to remind the user if they
have open todos — even when the |
| 27 app is closed.</p> |
| 28 |
| 29 <p>First, we must add a way for our app to regularly check for uncompleted todos
. Next, we need to display a message to the user, even if the Todo app window is
closed. To accomplish this, we will learn about implementing alarms and notific
ations in Chrome Apps.</p> |
| 30 |
| 31 <h2 id="alarms">Add alarms</h2> |
| 32 |
| 33 <p>Use <a href="/apps/alarms.html"><code>chrome.alarms</code></a> |
| 34 to set a wake up interval. As long as Chrome is running, the alarm listener will
be called at |
| 35 approximately the interval set.</p> |
| 36 |
| 37 <h3 id="update-permissions-alarms">Update app permissions</h3> |
| 38 |
| 39 <p>In <strong><em>manifest.json</em></strong>, request the <code>"alarms"</code>
permission:</p> |
| 40 |
| 41 <pre data-filename="manifest.json"> |
| 42 "permissions": ["storage"<b>, "alarms"</b>], |
| 43 </pre> |
| 44 |
| 45 <h3 id="update-background-script-alarms">Update background scripts</h3> |
| 46 |
| 47 <p>In <strong><em>background.js</em></strong>, add an <a href="/apps/alarms#even
t-onAlarm"><code>onAlarm</code></a> listener. |
| 48 For now, the callback function will just log a message to the Console whenever t
here is a todo item:</p> |
| 49 |
| 50 <pre data-filename="background.js"> |
| 51 chrome.app.runtime.onLaunched.addListener(function() { |
| 52 chrome.app.window.create('index.html', { |
| 53 id: 'main', |
| 54 bounds: { width: 620, height: 500 } |
| 55 }); |
| 56 }); |
| 57 <b>chrome.alarms.onAlarm.addListener(function( alarm ) { |
| 58 console.log("Got an alarm!", alarm); |
| 59 });</b> |
| 60 </pre> |
| 61 |
| 62 <h3 id="update-html-view-alarms">Update HTML view</h3> |
| 63 |
| 64 <p>In <strong><em>index.html</em></strong>, add an <strong>Activate alarm</stron
g> button:</p> |
| 65 |
| 66 <pre data-filename="index.html"> |
| 67 <footer id="info"> |
| 68 <b><button id="toggleAlarm">Activate alarm</button></b> |
| 69 ... |
| 70 </footer> |
| 71 </pre> |
| 72 |
| 73 <p>We now need to write the JavaScript event handler for this new button but rec
all from <a href="/apps/app_codelab_import_todomvc#csp-compliance">Step 2</a> th
at one of the most common CSP non-compliances is caused by inline JavaScript. St
ill in <em>index.html</em>, add this line to import a new <em>alarms.js</em> |
| 74 file we will create in the following step:</p> |
| 75 |
| 76 <pre data-filename="index.html"> |
| 77 </footer> |
| 78 ... |
| 79 <script src="js/app.js"></script> |
| 80 <b><script src="js/alarms.js"></script></b> |
| 81 </body> |
| 82 </pre> |
| 83 |
| 84 <h3 id="add-alarms-script">Create alarms script</h3> |
| 85 |
| 86 <p>Create a new file in your <strong><em>js</em></strong> folder called <strong>
<em>alarms.js</em></strong>.</p> |
| 87 |
| 88 <p>Use the code below to add <code>checkAlarm()</code>, <code>createAlarm()</cod
e>, |
| 89 <code>cancelAlarm()</code> and <code>toggleAlarm()</code> methods, along with a |
| 90 click event handler to toggle the |
| 91 alarm when the <strong>Activate alarm</strong> button is clicked.</p> |
| 92 |
| 93 <pre data-filename="alarms.js"> |
| 94 (function () { |
| 95 'use strict'; |
| 96 var alarmName = 'remindme'; |
| 97 function checkAlarm(callback) { |
| 98 chrome.alarms.getAll(function(alarms) { |
| 99 var hasAlarm = alarms.some(function(a) { |
| 100 return a.name == alarmName; |
| 101 }); |
| 102 var newLabel; |
| 103 if (hasAlarm) { |
| 104 newLabel = 'Cancel alarm'; |
| 105 } else { |
| 106 newLabel = 'Activate alarm'; |
| 107 } |
| 108 document.getElementById('toggleAlarm').innerText = newLabel; |
| 109 if (callback) callback(hasAlarm); |
| 110 }) |
| 111 } |
| 112 function createAlarm() { |
| 113 chrome.alarms.create(alarmName, { |
| 114 delayInMinutes: 0.1, periodInMinutes: 0.1}); |
| 115 } |
| 116 function cancelAlarm() { |
| 117 chrome.alarms.clear(alarmName); |
| 118 } |
| 119 function doToggleAlarm() { |
| 120 checkAlarm( function(hasAlarm) { |
| 121 if (hasAlarm) { |
| 122 cancelAlarm(); |
| 123 } else { |
| 124 createAlarm(); |
| 125 } |
| 126 checkAlarm(); |
| 127 }); |
| 128 } |
| 129 $$('#toggleAlarm').addEventListener('click', doToggleAlarm); |
| 130 checkAlarm(); |
| 131 })(); |
| 132 </pre> |
| 133 |
| 134 <p>Reload your app and spend a few moments activating (and deactivating) the ala
rm.</p> |
| 135 |
| 136 <p class="note">Since the log messages are being sent to the Console via the eve
nt page (aka background script), |
| 137 you need to <strong>right-click > Inspect Background Page</strong> to see the lo
g messages:</p> |
| 138 |
| 139 <figure> |
| 140 <img src="{{static}}/images/app_codelab/inspect-background-page.png" alt="Insp
ect background page messages"/> |
| 141 </figure> |
| 142 |
| 143 <p class="note">It's also worthwhile to continue to use <strong>right-click > In
spect Element</strong> |
| 144 to make sure you do not have errors in other JavaScript files.</p> |
| 145 |
| 146 <p>Whenever you have the alarm activated, you should see log messages being prin
ted |
| 147 in the Console every time the alarm "rings":</p> |
| 148 |
| 149 <figure> |
| 150 <img src="{{static}}/images/app_codelab/alarm-console-logs.png" alt="Alarm mes
sages printing to the Console"/> |
| 151 </figure> |
| 152 |
| 153 <p>You should notice that:</p> |
| 154 |
| 155 <ul> |
| 156 <li>Even when you close the Todo app window, the alarms will keep coming.</li> |
| 157 <li>On platforms other than ChromeOS, if you completely close all Chrome brows
er instances, alarms won't trigger.</li> |
| 158 </ul> |
| 159 |
| 160 <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> |
| 161 |
| 162 <h3 id="create-alarms">Create alarms</h3> |
| 163 |
| 164 <p>In <code>createAlarm()</code>, we use the <a href="/apps/alarms#method-create
"><code>chrome.alarms.create()</code></a> API to create an alarm when <strong>Ac
tivate alarm</strong> is toggled.</p> |
| 165 |
| 166 <pre data-filename="alarms.js"> |
| 167 chrome.alarms.create(alarmName, {delayInMinutes: 0.1, periodInMinutes: 0.1}); |
| 168 </pre> |
| 169 |
| 170 <p>The first parameter is an optional string identifying an unique name for your
alarm. In our case, the alarm name is <code>remindme</code>. (Note: You need to
set an alarm name in order to cancel it by name.)</p> |
| 171 |
| 172 <p>The second parameter is an <code>alarmInfo</code> object. Valid properties fo
r <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 m
achine, Chrome limits alarms to once every minute. |
| 173 We are using small values (0.1 of a minute) here for demo purposes only.</p> |
| 174 |
| 175 <h3 id="clear-alarms">Clear alarms</h3> |
| 176 |
| 177 <p>In <code>cancelAlarm()</code>, we use the <a href="/apps/alarms#method-clear"
><code>chrome.alarms.clear()</code></a> API to cancel an alarm when <strong>Canc
el alarm</strong> is toggled.</p> |
| 178 |
| 179 <pre data-filename="alarms.js"> |
| 180 chrome.alarms.clear(alarmName); |
| 181 </pre> |
| 182 |
| 183 <p>The first parameter should be the identifying string you used as an alarm nam
e in <code>chrome.alarms.create()</code>.</p> |
| 184 |
| 185 <p>The second (optional) parameter is a callback function that should take on th
e format:</p> |
| 186 |
| 187 <pre>function(boolean wasCleared) {...};</pre> |
| 188 |
| 189 <h3 id="get-alarms">Get alarms</h3> |
| 190 |
| 191 <p>In <code>checkAlarm()</code>, we use the <a href="/apps/alarms#method-getAll"
><code>chrome.alarms.getAll()</code></a> API to get an array of all created alar
ms in order to update the UI state of the toggle button.</p> |
| 192 |
| 193 <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 insp
ect running alarms in the DevTools Console like so:</p> |
| 194 |
| 195 <pre> |
| 196 chrome.alarms.getAll(function(alarms) { |
| 197 console.log(alarms); |
| 198 console.log(alarms[0]); |
| 199 }); |
| 200 </pre> |
| 201 |
| 202 <p>This will output an object such as <code>{name: "remindme", periodInMinutes:
0.1, scheduledTime: 1397587981166.858}</code> as seen below:</p> |
| 203 |
| 204 <figure> |
| 205 <img src="{{static}}/images/app_codelab/alarms-getAll-console.png" alt="Use ge
tAll() to inspect running alarms."/> |
| 206 </figure> |
| 207 |
| 208 <h3 id="next-section">Get ready for the next section</h3> |
| 209 |
| 210 <p>Now that we have alarms in place to poll our app at regular intervals, we can
use it as a base for adding visual notifications — which is exactly what
we'll do in the next section.</p> |
| 211 |
| 212 <h2 id="notifications">Add notifications</h2> |
| 213 |
| 214 <p>Let's change the alarm notification to something the user can easily notice. |
| 215 For that purpose, we will use <a href="/apps/notifications"><code>chrome.notific
ations</code></a> |
| 216 to show a desktop notification like the one below:</p> |
| 217 |
| 218 <figure> |
| 219 <img src="{{static}}/images/app_codelab/notification-example.png" alt="Our Tod
o app notification"/> |
| 220 </figure> |
| 221 |
| 222 <p>When the user clicks on the notification, we will bring the Todo app window i
nto view.</p> |
| 223 |
| 224 <h3 id="update-permissions-notifications">Update app permissions</h3> |
| 225 |
| 226 <p>In <strong><em>manifest.json</em></strong>, request the <code>"notifications"
</code> permission:</p> |
| 227 |
| 228 <pre data-filename="manifest.json"> |
| 229 "permissions": ["storage", "alarms"<b>, "notifications"</b>], |
| 230 </pre> |
| 231 |
| 232 <h3 id="update-background-script-notifications">Update background scripts</h3> |
| 233 |
| 234 <p>In <strong><em>background.js</em></strong>, refactor the <code>chrome.app.win
dow.create()</code> callback into a standalone method so we can reuse it:</p> |
| 235 |
| 236 <pre data-filename="background.js"> |
| 237 <strike>chrome.app.runtime.onLaunched.addListener(function() {</strike> |
| 238 <b>function launch() {</b> |
| 239 chrome.app.window.create('index.html', { |
| 240 id: 'main', |
| 241 bounds: { width: 620, height: 500 } |
| 242 }); |
| 243 <b>}</b> |
| 244 <strike>});</strike> |
| 245 <b>chrome.app.runtime.onLaunched.addListener(launch);</b> |
| 246 ... |
| 247 </pre> |
| 248 |
| 249 <h3 id="update-alarm-listener">Update alarm listener</h3> |
| 250 |
| 251 <p>At the top of the <em>background.js</em>, add a variable for a database name
that we'll use in our alarm listener:</p> |
| 252 |
| 253 <pre data-filename="background.js"> |
| 254 <b>var dbName = 'todos-vanillajs';</b> |
| 255 </pre> |
| 256 |
| 257 <p>The value of <code>dbName</code> is the same database name we set in line 17
of <em>js/app.js</em>:</p> |
| 258 |
| 259 <pre data-filename="app.js"> |
| 260 var todo = new Todo('todos-vanillajs'); |
| 261 </pre> |
| 262 |
| 263 <h3 id="create-notification">Create a notification</h3> |
| 264 |
| 265 <p>Instead of simply logging a new alarm to the Console, update the <code>onAlar
m</code> listener |
| 266 to get stored data via <code>chrome.storage.local.get()</code> and call a <code>
showNotification()</code> |
| 267 method that we'll make in the following step:</p> |
| 268 |
| 269 <pre data-filename="background.js"> |
| 270 chrome.alarms.onAlarm.addListener(function( alarm ) { |
| 271 <strike>console.log("Got an alarm!", alarm);</strike> |
| 272 <b>chrome.storage.local.get(dbName, showNotification);</b> |
| 273 }); |
| 274 </pre> |
| 275 |
| 276 <p>Add this <code>showNotification()</code> method to <em>background.js</em>:</p
> |
| 277 |
| 278 <pre data-filename="background.js"> |
| 279 function launch(){ |
| 280 ... |
| 281 } |
| 282 |
| 283 <b>function showNotification(storedData) { |
| 284 var openTodos = 0; |
| 285 if ( storedData[dbName].todos ) { |
| 286 storedData[dbName].todos.forEach(function(todo) { |
| 287 if ( !todo.completed ) { |
| 288 openTodos++; |
| 289 } |
| 290 }); |
| 291 } |
| 292 if (openTodos>0) { |
| 293 // Now create the notification |
| 294 chrome.notifications.create('reminder', { |
| 295 type: 'basic', |
| 296 iconUrl: 'icon_128.png', |
| 297 title: 'Don\'t forget!', |
| 298 message: 'You have '+openTodos+' things to do. Wake up, dude!' |
| 299 }, function(notificationId) {}); |
| 300 } |
| 301 }</b> |
| 302 |
| 303 chrome.app.runtime.onLaunched.addListener(launch); |
| 304 ... |
| 305 </pre> |
| 306 |
| 307 <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 hre
f="/apps/notifications#method-create"><code>chrome.notifications.create()</code>
</a>.</p> |
| 308 |
| 309 <p>The first parameter is an uniquely identifying notification name. You must ha
ve an id set in order to clear or handle interactions with that particular notif
ication. If the id matches an existing notification, <code>create()</code> first
clears that notification before making a new notification.</p> |
| 310 |
| 311 <p>The second parameter is a <a href="/apps/notifications#type-NotificationOptio
ns"><code>NotificationOptions</code></a> object. There are many options for rend
ering the notification popup. Here we are using a "basic" notification with an i
con, title, and message. Other notification types include images, lists, and pro
gress indicators. Feel free to return to this section when you are done Step 3 a
nd experiment with other notification features.</p> |
| 312 |
| 313 <p>The third (optional) parameter is a callback method that should take on the f
ormat:</p> |
| 314 |
| 315 <pre> |
| 316 function(string notificationId) {...}; |
| 317 </pre> |
| 318 |
| 319 <h3 id="interact-with-notification">Handle notification interactions</h3> |
| 320 |
| 321 <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"><cod
e>chrome.notifications.onClicked</code></a> event handler:</p> |
| 322 |
| 323 <pre data-filename="background.js"> |
| 324 <b>chrome.notifications.onClicked.addListener(function() { |
| 325 launch(); |
| 326 });</b> |
| 327 </pre> |
| 328 |
| 329 <p>The event handler callback simply calls the <code>launch()</code> method that
we refactored earlier. <code>chrome.app.window.create()</code> will either crea
te a new Chrome App window if one doesn't already exist, or bring into focus the
currently open window that has the window id of <code>main</code>.</p> |
| 330 |
| 331 <!-- <h3 id="clear-notification">Clear the notification</h3> |
| 332 |
| 333 <p style="color: red;">You might notice that the notification popup does not go
away after we click on it. The <code>onClicked</code> event handler callback pas
ses along the id name of the popup that was interacted with. Use that, in combin
ation with <a href=""><code>chrome.notifications.clear()</code></a>, to dismiss
the notification:</p> |
| 334 |
| 335 <pre data-filename="background.js"> |
| 336 chrome.notifications.onClicked.addListener( |
| 337 function(<b> notificationId </b>) { |
| 338 launch(); |
| 339 <b>chrome.notifications.clear(notificationId, function() {});</b> |
| 340 } |
| 341 ); |
| 342 </pre> --> |
| 343 |
| 344 <!-- <h3 id="rich-notifications">Learn more about rich notifications</h3> |
| 345 |
| 346 <p>For additional information about notifications, refer to <a href="/apps/deskt
op_notifications">Rich Notifications</a> in the docs.</p> --> |
| 347 |
| 348 <h2 id="launch">Launch your finished Todo app</h2> |
| 349 |
| 350 <p>You are done Step 3! Reload your app and you should now have a Todo app with
reminders.</p> |
| 351 |
| 352 <figure> |
| 353 <img src="{{static}}/images/app_codelab/step3-completed.gif" alt="The Todo app
with notifications"/> |
| 354 </figure> |
| 355 |
| 356 <p>You should notice that:</p> |
| 357 |
| 358 <ul> |
| 359 <li>If you don't have any uncompleted todo items, there are no popup notificat
ions.</li> |
| 360 <li>If you click on the notification when your app is closed, the Todo app wil
l |
| 361 open or come into focus.</li> |
| 362 </ul> |
| 363 |
| 364 <h3 id="troubleshooting">Troubleshooting</h3> |
| 365 |
| 366 <p>Your final <em>background.js</em> file should look like |
| 367 <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 |
| 368 higher. If notifications still don't show up, check for error messages in the |
| 369 DevTools Console on both the main window (<strong>right click > Inspect Element<
/strong>) and the |
| 370 background page (<strong>right click > Inspect Background Page</strong>).</p> |
| 371 |
| 372 <h2 id="recap">Recap APIs referenced in this step</h2> |
| 373 |
| 374 <p>For more detailed information about some of the APIs introduced in this step,
refer to:</p> |
| 375 |
| 376 <ul> |
| 377 <li> |
| 378 <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the
Chrome developer docs">Declare Permissions</a> |
| 379 <a href="#update-permissions-alarms" class="anchor-link-icon" title="This fe
ature mentioned in 'Update app permissions for alarms'">↑</a> <a href="#up
date-permissions-notifications" class="anchor-link-icon" title="This feature men
tioned in 'Update app permissions for notifications'">↑</a> |
| 380 </li> |
| 381 <li> |
| 382 <a href="/apps/alarms.html" title="Read 'chrome.alarms' in the Chrome develo
per docs">chrome.alarms</a> |
| 383 <a href="#alarms" class="anchor-link-icon" title="This feature mentioned in
'Add alarm reminders'">↑</a> |
| 384 </li> |
| 385 <li> |
| 386 <a href="/apps/alarms#event-onAlarm" title="Read 'chrome.alarms.onAlarm' in
the Chrome developer docs">chrome.alarms.onAlarm</a> |
| 387 <a href="#update-background-script-alarms" class="anchor-link-icon" title="T
his feature mentioned in ''">↑</a> |
| 388 </li> |
| 389 <li> |
| 390 <a href="/apps/alarms#method-create" title="Read 'chrome.alarms.create()' in
the Chrome developer docs">chrome.alarms.create()</a> |
| 391 <a href="#create-alarms" class="anchor-link-icon" title="This feature mentio
ned in 'Create alarms'">↑</a> |
| 392 </li> |
| 393 <li> |
| 394 <a href="/apps/alarms#method-clear" title="Read 'chrome.alarms.clear()' in t
he Chrome developer docs">chrome.alarms.clear()</a> |
| 395 <a href="#clear-alarms" class="anchor-link-icon" title="This feature mention
ed in 'Clear alarms'">↑</a> |
| 396 </li> |
| 397 <li> |
| 398 <a href="/apps/alarms#method-getAll" title="Read 'chrome.alarms.getAll()' in
the Chrome developer docs">chrome.alarms.getAll()</a> |
| 399 <a href="#get-alarms" class="anchor-link-icon" title="This feature mentioned
in 'Get alarms'">↑</a> |
| 400 </li> |
| 401 <li> |
| 402 <a href="/apps/notifications" title="Read 'chrome.notifications' in the Chro
me developer docs">chrome.notifications</a> |
| 403 <a href="#notifications" class="anchor-link-icon" title="This feature mentio
ned in 'Add notifications'">↑</a> |
| 404 </li> |
| 405 <li> |
| 406 <a href="/apps/notifications#method-create" title="Read 'chrome.notification
s.create()' in the Chrome developer docs">chrome.notifications.create()</a> |
| 407 <a href="#create-notification" class="anchor-link-icon" title="This feature
mentioned in 'Create a notification'">↑</a> |
| 408 </li> |
| 409 <li> |
| 410 <a href="/apps/notifications#type-NotificationOptions" title="Read 'Notifica
tionOptions' in the Chrome developer docs">NotificationOptions</a> |
| 411 <a href="#create-notification" class="anchor-link-icon" title="This feature
mentioned in 'Create a notification'">↑</a> |
| 412 </li> |
| 413 <li> |
| 414 <a href="/apps/notifications#event-onClicked" title="Read 'chrome.notificati
ons.onClicked' in the Chrome developer docs">chrome.notifications.onClicked</a> |
| 415 <a href="#interact-with-notification" class="anchor-link-icon" title="This f
eature mentioned in 'Handle notification interactions'">↑</a> |
| 416 </li> |
| 417 <!-- <li> |
| 418 <a href="/apps/notifications" title="Read 'chrome.notifications.clear()' in
the Chrome developer docs">chrome.notifications.clear()</a> |
| 419 <a href="#clear-notification" class="anchor-link-icon" title="This feature m
entioned in 'Clear the notification'">↑</a> |
| 420 </li> --> |
| 421 <!-- <li> |
| 422 <a href="/apps/desktop_notifications" title="Read 'Rich Notifications' in th
e Chrome developer docs">Rich Notifications</a> |
| 423 <a href="#rich-notifications" class="anchor-link-icon" title="This feature m
entioned in 'Learn more about rich notifications'">↑</a> |
| 424 </li> --> |
| 425 </ul> |
| 426 |
| 427 <p>Ready to continue onto the next step? Go to <a href="app_codelab_webview.html
">Step 4 - Open external links with a webview »</a></p> |
OLD | NEW |