Index: chrome/common/extensions/docs/templates/articles/app_codelab_alarms.html |
diff --git a/chrome/common/extensions/docs/templates/articles/app_codelab_alarms.html b/chrome/common/extensions/docs/templates/articles/app_codelab_alarms.html |
new file mode 100644 |
index 0000000000000000000000000000000000000000..22032f8d52ee1de54c9235de4d24ec7c643d1fcd |
--- /dev/null |
+++ b/chrome/common/extensions/docs/templates/articles/app_codelab_alarms.html |
@@ -0,0 +1,427 @@ |
+<h1 id="add-alarms-notifications"> |
+ <span class="h1-step">Step 3:</span> |
+ Add Alarms and Notifications |
+</h1> |
+ |
+<p class="note"> |
+ <strong>Want to start fresh from here?</strong> |
+ 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>. |
+</p> |
+ |
+<p>In this step, you will learn:</p> |
+ |
+<ul> |
+ <li>How to wake your app at specified intervals to execute background tasks.</li> |
+ <li>How to use on-screen notifications to draw attention to something important.</li> |
+</ul> |
+ |
+<p> |
+ <em>Estimated time to complete this step: 20 minutes.</em> |
+ <br> |
+ To preview what you will complete in this step, <a href="#launch">jump down to the bottom of this page ↓</a>. |
+</p> |
+ |
+<h2 id="overview">Enhance your Todo app with reminders</h2> |
+ |
+<p>Let's enhance our Todo app by adding functionality to remind the user if they have open todos — even when the |
+app is closed.</p> |
+ |
+<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 notifications in Chrome Apps.</p> |
+ |
+<h2 id="alarms">Add alarms</h2> |
+ |
+<p>Use <a href="/apps/alarms.html"><code>chrome.alarms</code></a> |
+to set a wake up interval. As long as Chrome is running, the alarm listener will be called at |
+approximately the interval set.</p> |
+ |
+<h3 id="update-permissions-alarms">Update app permissions</h3> |
+ |
+<p>In <strong><em>manifest.json</em></strong>, request the <code>"alarms"</code> permission:</p> |
+ |
+<pre data-filename="manifest.json"> |
+"permissions": ["storage"<b>, "alarms"</b>], |
+</pre> |
+ |
+<h3 id="update-background-script-alarms">Update background scripts</h3> |
+ |
+<p>In <strong><em>background.js</em></strong>, add an <a href="/apps/alarms#event-onAlarm"><code>onAlarm</code></a> listener. |
+For now, the callback function will just log a message to the Console whenever there is a todo item:</p> |
+ |
+<pre data-filename="background.js"> |
+chrome.app.runtime.onLaunched.addListener(function() { |
+ chrome.app.window.create('index.html', { |
+ id: 'main', |
+ bounds: { width: 620, height: 500 } |
+ }); |
+}); |
+<b>chrome.alarms.onAlarm.addListener(function( alarm ) { |
+ console.log("Got an alarm!", alarm); |
+});</b> |
+</pre> |
+ |
+<h3 id="update-html-view-alarms">Update HTML view</h3> |
+ |
+<p>In <strong><em>index.html</em></strong>, add an <strong>Activate alarm</strong> button:</p> |
+ |
+<pre data-filename="index.html"> |
+<footer id="info"> |
+ <b><button id="toggleAlarm">Activate alarm</button></b> |
+ ... |
+</footer> |
+</pre> |
+ |
+<p>We now need to write the JavaScript event handler for this new button but recall from <a href="/apps/app_codelab_import_todomvc#csp-compliance">Step 2</a> that one of the most common CSP non-compliances is caused by inline JavaScript. Still in <em>index.html</em>, add this line to import a new <em>alarms.js</em> |
+file we will create in the following step:</p> |
+ |
+<pre data-filename="index.html"> |
+ </footer> |
+ ... |
+ <script src="js/app.js"></script> |
+ <b><script src="js/alarms.js"></script></b> |
+</body> |
+</pre> |
+ |
+<h3 id="add-alarms-script">Create alarms script</h3> |
+ |
+<p>Create a new file in your <strong><em>js</em></strong> folder called <strong><em>alarms.js</em></strong>.</p> |
+ |
+<p>Use the code below to add <code>checkAlarm()</code>, <code>createAlarm()</code>, |
+<code>cancelAlarm()</code> and <code>toggleAlarm()</code> methods, along with a |
+click event handler to toggle the |
+alarm when the <strong>Activate alarm</strong> button is clicked.</p> |
+ |
+<pre data-filename="alarms.js"> |
+(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(); |
+})(); |
+</pre> |
+ |
+<p>Reload your app and spend a few moments activating (and deactivating) the alarm.</p> |
+ |
+<p class="note">Since the log messages are being sent to the Console via the event page (aka background script), |
+you need to <strong>right-click > Inspect Background Page</strong> to see the log messages:</p> |
+ |
+<figure> |
+ <img src="{{static}}/images/app_codelab/inspect-background-page.png" alt="Inspect background page messages"/> |
+</figure> |
+ |
+<p class="note">It's also worthwhile to continue to use <strong>right-click > Inspect Element</strong> |
+to make sure you do not have errors in other JavaScript files.</p> |
+ |
+<p>Whenever you have the alarm activated, you should see log messages being printed |
+in the Console every time the alarm "rings":</p> |
+ |
+<figure> |
+ <img src="{{static}}/images/app_codelab/alarm-console-logs.png" alt="Alarm messages printing to the Console"/> |
+</figure> |
+ |
+<p>You should notice that:</p> |
+ |
+<ul> |
+ <li>Even when you close the Todo app window, the alarms will keep coming.</li> |
+ <li>On platforms other than ChromeOS, if you completely close all Chrome browser instances, alarms won't trigger.</li> |
+</ul> |
+ |
+<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> |
+ |
+<h3 id="create-alarms">Create alarms</h3> |
+ |
+<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>Activate alarm</strong> is toggled.</p> |
+ |
+<pre data-filename="alarms.js"> |
+chrome.alarms.create(alarmName, {delayInMinutes: 0.1, periodInMinutes: 0.1}); |
+</pre> |
+ |
+<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> |
+ |
+<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. |
+We are using small values (0.1 of a minute) here for demo purposes only.</p> |
+ |
+<h3 id="clear-alarms">Clear alarms</h3> |
+ |
+<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>Cancel alarm</strong> is toggled.</p> |
+ |
+<pre data-filename="alarms.js"> |
+chrome.alarms.clear(alarmName); |
+</pre> |
+ |
+<p>The first parameter should be the identifying string you used as an alarm name in <code>chrome.alarms.create()</code>.</p> |
+ |
+<p>The second (optional) parameter is a callback function that should take on the format:</p> |
+ |
+<pre>function(boolean wasCleared) {...};</pre> |
+ |
+<h3 id="get-alarms">Get alarms</h3> |
+ |
+<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 alarms in order to update the UI state of the toggle button.</p> |
+ |
+<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> |
+ |
+<pre> |
+chrome.alarms.getAll(function(alarms) { |
+ console.log(alarms); |
+ console.log(alarms[0]); |
+}); |
+</pre> |
+ |
+<p>This will output an object such as <code>{name: "remindme", periodInMinutes: 0.1, scheduledTime: 1397587981166.858}</code> as seen below:</p> |
+ |
+<figure> |
+ <img src="{{static}}/images/app_codelab/alarms-getAll-console.png" alt="Use getAll() to inspect running alarms."/> |
+</figure> |
+ |
+<h3 id="next-section">Get ready for the next section</h3> |
+ |
+<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> |
+ |
+<h2 id="notifications">Add notifications</h2> |
+ |
+<p>Let's change the alarm notification to something the user can easily notice. |
+For that purpose, we will use <a href="/apps/notifications"><code>chrome.notifications</code></a> |
+to show a desktop notification like the one below:</p> |
+ |
+<figure> |
+ <img src="{{static}}/images/app_codelab/notification-example.png" alt="Our Todo app notification"/> |
+</figure> |
+ |
+<p>When the user clicks on the notification, we will bring the Todo app window into view.</p> |
+ |
+<h3 id="update-permissions-notifications">Update app permissions</h3> |
+ |
+<p>In <strong><em>manifest.json</em></strong>, request the <code>"notifications"</code> permission:</p> |
+ |
+<pre data-filename="manifest.json"> |
+"permissions": ["storage", "alarms"<b>, "notifications"</b>], |
+</pre> |
+ |
+<h3 id="update-background-script-notifications">Update background scripts</h3> |
+ |
+<p>In <strong><em>background.js</em></strong>, refactor the <code>chrome.app.window.create()</code> callback into a standalone method so we can reuse it:</p> |
+ |
+<pre data-filename="background.js"> |
+<strike>chrome.app.runtime.onLaunched.addListener(function() {</strike> |
+<b>function launch() {</b> |
+ chrome.app.window.create('index.html', { |
+ id: 'main', |
+ bounds: { width: 620, height: 500 } |
+ }); |
+<b>}</b> |
+<strike>});</strike> |
+<b>chrome.app.runtime.onLaunched.addListener(launch);</b> |
+... |
+</pre> |
+ |
+<h3 id="update-alarm-listener">Update alarm listener</h3> |
+ |
+<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> |
+ |
+<pre data-filename="background.js"> |
+<b>var dbName = 'todos-vanillajs';</b> |
+</pre> |
+ |
+<p>The value of <code>dbName</code> is the same database name we set in line 17 of <em>js/app.js</em>:</p> |
+ |
+<pre data-filename="app.js"> |
+var todo = new Todo('todos-vanillajs'); |
+</pre> |
+ |
+<h3 id="create-notification">Create a notification</h3> |
+ |
+<p>Instead of simply logging a new alarm to the Console, update the <code>onAlarm</code> listener |
+to get stored data via <code>chrome.storage.local.get()</code> and call a <code>showNotification()</code> |
+method that we'll make in the following step:</p> |
+ |
+<pre data-filename="background.js"> |
+chrome.alarms.onAlarm.addListener(function( alarm ) { |
+ <strike>console.log("Got an alarm!", alarm);</strike> |
+ <b>chrome.storage.local.get(dbName, showNotification);</b> |
+}); |
+</pre> |
+ |
+<p>Add this <code>showNotification()</code> method to <em>background.js</em>:</p> |
+ |
+<pre data-filename="background.js"> |
+function launch(){ |
+ ... |
+} |
+ |
+<b>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) {}); |
+ } |
+}</b> |
+ |
+chrome.app.runtime.onLaunched.addListener(launch); |
+... |
+</pre> |
+ |
+<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> |
+ |
+<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> |
+ |
+<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> |
+ |
+<p>The third (optional) parameter is a callback method that should take on the format:</p> |
+ |
+<pre> |
+function(string notificationId) {...}; |
+</pre> |
+ |
+<h3 id="interact-with-notification">Handle notification interactions</h3> |
+ |
+<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> |
+ |
+<pre data-filename="background.js"> |
+<b>chrome.notifications.onClicked.addListener(function() { |
+ launch(); |
+});</b> |
+</pre> |
+ |
+<p>The event handler callback simply calls the <code>launch()</code> method that we refactored earlier. <code>chrome.app.window.create()</code> will either create 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> |
+ |
+<!-- <h3 id="clear-notification">Clear the notification</h3> |
+ |
+<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 passes along the id name of the popup that was interacted with. Use that, in combination with <a href=""><code>chrome.notifications.clear()</code></a>, to dismiss the notification:</p> |
+ |
+<pre data-filename="background.js"> |
+chrome.notifications.onClicked.addListener( |
+ function(<b> notificationId </b>) { |
+ launch(); |
+ <b>chrome.notifications.clear(notificationId, function() {});</b> |
+ } |
+); |
+</pre> --> |
+ |
+<!-- <h3 id="rich-notifications">Learn more about rich notifications</h3> |
+ |
+<p>For additional information about notifications, refer to <a href="/apps/desktop_notifications">Rich Notifications</a> in the docs.</p> --> |
+ |
+<h2 id="launch">Launch your finished Todo app</h2> |
+ |
+<p>You are done Step 3! Reload your app and you should now have a Todo app with reminders.</p> |
+ |
+<figure> |
+ <img src="{{static}}/images/app_codelab/step3-completed.gif" alt="The Todo app with notifications"/> |
+</figure> |
+ |
+<p>You should notice that:</p> |
+ |
+<ul> |
+ <li>If you don't have any uncompleted todo items, there are no popup notifications.</li> |
+ <li>If you click on the notification when your app is closed, the Todo app will |
+ open or come into focus.</li> |
+</ul> |
+ |
+<h3 id="troubleshooting">Troubleshooting</h3> |
+ |
+<p>Your final <em>background.js</em> file should look like |
+<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 |
+higher. If notifications still don't show up, check for error messages in the |
+DevTools Console on both the main window (<strong>right click > Inspect Element</strong>) and the |
+background page (<strong>right click > Inspect Background Page</strong>).</p> |
+ |
+<h2 id="recap">Recap APIs referenced in this step</h2> |
+ |
+<p>For more detailed information about some of the APIs introduced in this step, refer to:</p> |
+ |
+<ul> |
+ <li> |
+ <a href="/apps/declare_permissions" title="Read 'Declare Permissions' in the Chrome developer docs">Declare Permissions</a> |
+ <a href="#update-permissions-alarms" class="anchor-link-icon" title="This feature mentioned in 'Update app permissions for alarms'">↑</a> <a href="#update-permissions-notifications" class="anchor-link-icon" title="This feature mentioned in 'Update app permissions for notifications'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/alarms.html" title="Read 'chrome.alarms' in the Chrome developer docs">chrome.alarms</a> |
+ <a href="#alarms" class="anchor-link-icon" title="This feature mentioned in 'Add alarm reminders'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/alarms#event-onAlarm" title="Read 'chrome.alarms.onAlarm' in the Chrome developer docs">chrome.alarms.onAlarm</a> |
+ <a href="#update-background-script-alarms" class="anchor-link-icon" title="This feature mentioned in ''">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/alarms#method-create" title="Read 'chrome.alarms.create()' in the Chrome developer docs">chrome.alarms.create()</a> |
+ <a href="#create-alarms" class="anchor-link-icon" title="This feature mentioned in 'Create alarms'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/alarms#method-clear" title="Read 'chrome.alarms.clear()' in the Chrome developer docs">chrome.alarms.clear()</a> |
+ <a href="#clear-alarms" class="anchor-link-icon" title="This feature mentioned in 'Clear alarms'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/alarms#method-getAll" title="Read 'chrome.alarms.getAll()' in the Chrome developer docs">chrome.alarms.getAll()</a> |
+ <a href="#get-alarms" class="anchor-link-icon" title="This feature mentioned in 'Get alarms'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/notifications" title="Read 'chrome.notifications' in the Chrome developer docs">chrome.notifications</a> |
+ <a href="#notifications" class="anchor-link-icon" title="This feature mentioned in 'Add notifications'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/notifications#method-create" title="Read 'chrome.notifications.create()' in the Chrome developer docs">chrome.notifications.create()</a> |
+ <a href="#create-notification" class="anchor-link-icon" title="This feature mentioned in 'Create a notification'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/notifications#type-NotificationOptions" title="Read 'NotificationOptions' in the Chrome developer docs">NotificationOptions</a> |
+ <a href="#create-notification" class="anchor-link-icon" title="This feature mentioned in 'Create a notification'">↑</a> |
+ </li> |
+ <li> |
+ <a href="/apps/notifications#event-onClicked" title="Read 'chrome.notifications.onClicked' in the Chrome developer docs">chrome.notifications.onClicked</a> |
+ <a href="#interact-with-notification" class="anchor-link-icon" title="This feature mentioned in 'Handle notification interactions'">↑</a> |
+ </li> |
+ <!-- <li> |
+ <a href="/apps/notifications" title="Read 'chrome.notifications.clear()' in the Chrome developer docs">chrome.notifications.clear()</a> |
+ <a href="#clear-notification" class="anchor-link-icon" title="This feature mentioned in 'Clear the notification'">↑</a> |
+ </li> --> |
+ <!-- <li> |
+ <a href="/apps/desktop_notifications" title="Read 'Rich Notifications' in the Chrome developer docs">Rich Notifications</a> |
+ <a href="#rich-notifications" class="anchor-link-icon" title="This feature mentioned in 'Learn more about rich notifications'">↑</a> |
+ </li> --> |
+</ul> |
+ |
+<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> |