OLD | NEW |
1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 'use strict'; | 5 'use strict'; |
6 | 6 |
7 /** | 7 /** |
8 * @fileoverview The event page for Google Now for Chrome implementation. | 8 * @fileoverview The event page for Google Now for Chrome implementation. |
9 * The Google Now event page gets Google Now cards from the server and shows | 9 * The Google Now event page gets Google Now cards from the server and shows |
10 * them as Chrome notifications. | 10 * them as Chrome notifications. |
(...skipping 12 matching lines...) Expand all Loading... |
23 // TODO(vadimt): Honor the flag the enables Google Now integration. | 23 // TODO(vadimt): Honor the flag the enables Google Now integration. |
24 // TODO(vadimt): Figure out the final values of the constants. | 24 // TODO(vadimt): Figure out the final values of the constants. |
25 // TODO(vadimt): Remove 'console' calls. | 25 // TODO(vadimt): Remove 'console' calls. |
26 // TODO(vadimt): Consider sending JS stacks for malformed server responses. | 26 // TODO(vadimt): Consider sending JS stacks for malformed server responses. |
27 | 27 |
28 /** | 28 /** |
29 * Standard response code for successful HTTP requests. This is the only success | 29 * Standard response code for successful HTTP requests. This is the only success |
30 * code the server will send. | 30 * code the server will send. |
31 */ | 31 */ |
32 var HTTP_OK = 200; | 32 var HTTP_OK = 200; |
| 33 var HTTP_NOCONTENT = 204; |
33 | 34 |
34 var HTTP_BAD_REQUEST = 400; | 35 var HTTP_BAD_REQUEST = 400; |
35 var HTTP_UNAUTHORIZED = 401; | 36 var HTTP_UNAUTHORIZED = 401; |
36 var HTTP_FORBIDDEN = 403; | 37 var HTTP_FORBIDDEN = 403; |
37 var HTTP_METHOD_NOT_ALLOWED = 405; | 38 var HTTP_METHOD_NOT_ALLOWED = 405; |
38 | 39 |
| 40 var MS_IN_SECOND = 1000; |
| 41 var MS_IN_MINUTE = 60 * 1000; |
| 42 |
39 /** | 43 /** |
40 * Initial period for polling for Google Now Notifications cards to use when the | 44 * Initial period for polling for Google Now Notifications cards to use when the |
41 * period from the server is not available. | 45 * period from the server is not available. |
42 */ | 46 */ |
43 var INITIAL_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes | 47 var INITIAL_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes |
44 | 48 |
45 /** | 49 /** |
| 50 * Mininal period for polling for Google Now Notifications cards. |
| 51 */ |
| 52 var MINIMUM_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes |
| 53 |
| 54 /** |
46 * Maximal period for polling for Google Now Notifications cards to use when the | 55 * Maximal period for polling for Google Now Notifications cards to use when the |
47 * period from the server is not available. | 56 * period from the server is not available. |
48 */ | 57 */ |
49 var MAXIMUM_POLLING_PERIOD_SECONDS = 60 * 60; // 1 hour | 58 var MAXIMUM_POLLING_PERIOD_SECONDS = 60 * 60; // 1 hour |
50 | 59 |
51 /** | 60 /** |
52 * Initial period for retrying the server request for dismissing cards. | 61 * Initial period for retrying the server request for dismissing cards. |
53 */ | 62 */ |
54 var INITIAL_RETRY_DISMISS_PERIOD_SECONDS = 60; // 1 minute | 63 var INITIAL_RETRY_DISMISS_PERIOD_SECONDS = 60; // 1 minute |
55 | 64 |
(...skipping 24 matching lines...) Expand all Loading... |
80 | 89 |
81 var WELCOME_TOAST_NOTIFICATION_ID = 'enable-now-toast'; | 90 var WELCOME_TOAST_NOTIFICATION_ID = 'enable-now-toast'; |
82 | 91 |
83 /** | 92 /** |
84 * The indices of the buttons that are displayed on the welcome toast. | 93 * The indices of the buttons that are displayed on the welcome toast. |
85 * @enum {number} | 94 * @enum {number} |
86 */ | 95 */ |
87 var ToastButtonIndex = {YES: 0, NO: 1}; | 96 var ToastButtonIndex = {YES: 0, NO: 1}; |
88 | 97 |
89 /** | 98 /** |
| 99 * Notification as it's sent by the server. |
| 100 * |
| 101 * @typedef {{ |
| 102 * notificationId: string, |
| 103 * chromeNotificationId: string, |
| 104 * trigger: Object=, |
| 105 * version: number, |
| 106 * chromeNotificationOptions: Object, |
| 107 * actionUrls: Object=, |
| 108 * dismissal: Object |
| 109 * }} |
| 110 */ |
| 111 var UnmergedNotification; |
| 112 |
| 113 /** |
| 114 * Notification group as the client stores it. |cardsTimestamp| and |rank| are |
| 115 * defined if |cards| is non-empty. |
| 116 * |
| 117 * @typedef {{ |
| 118 * cards: Array.<UnmergedNotification>, |
| 119 * cardsTimestamp: number=, |
| 120 * nextPollTime: number, |
| 121 * rank: number= |
| 122 * }} |
| 123 */ |
| 124 var StorageGroup; |
| 125 |
| 126 /** |
90 * Checks if a new task can't be scheduled when another task is already | 127 * Checks if a new task can't be scheduled when another task is already |
91 * scheduled. | 128 * scheduled. |
92 * @param {string} newTaskName Name of the new task. | 129 * @param {string} newTaskName Name of the new task. |
93 * @param {string} scheduledTaskName Name of the scheduled task. | 130 * @param {string} scheduledTaskName Name of the scheduled task. |
94 * @return {boolean} Whether the new task conflicts with the existing task. | 131 * @return {boolean} Whether the new task conflicts with the existing task. |
95 */ | 132 */ |
96 function areTasksConflicting(newTaskName, scheduledTaskName) { | 133 function areTasksConflicting(newTaskName, scheduledTaskName) { |
97 if (newTaskName == UPDATE_CARDS_TASK_NAME && | 134 if (newTaskName == UPDATE_CARDS_TASK_NAME && |
98 scheduledTaskName == UPDATE_CARDS_TASK_NAME) { | 135 scheduledTaskName == UPDATE_CARDS_TASK_NAME) { |
99 // If a card update is requested while an old update is still scheduled, we | 136 // If a card update is requested while an old update is still scheduled, we |
(...skipping 11 matching lines...) Expand all Loading... |
111 } | 148 } |
112 | 149 |
113 return false; | 150 return false; |
114 } | 151 } |
115 | 152 |
116 var tasks = buildTaskManager(areTasksConflicting); | 153 var tasks = buildTaskManager(areTasksConflicting); |
117 | 154 |
118 // Add error processing to API calls. | 155 // Add error processing to API calls. |
119 wrapper.instrumentChromeApiFunction('location.onLocationUpdate.addListener', 0); | 156 wrapper.instrumentChromeApiFunction('location.onLocationUpdate.addListener', 0); |
120 wrapper.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1); | 157 wrapper.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1); |
| 158 wrapper.instrumentChromeApiFunction('notifications.clear', 1); |
121 wrapper.instrumentChromeApiFunction('notifications.create', 2); | 159 wrapper.instrumentChromeApiFunction('notifications.create', 2); |
122 wrapper.instrumentChromeApiFunction('notifications.update', 2); | 160 wrapper.instrumentChromeApiFunction('notifications.update', 2); |
123 wrapper.instrumentChromeApiFunction('notifications.getAll', 0); | 161 wrapper.instrumentChromeApiFunction('notifications.getAll', 0); |
124 wrapper.instrumentChromeApiFunction( | 162 wrapper.instrumentChromeApiFunction( |
125 'notifications.onButtonClicked.addListener', 0); | 163 'notifications.onButtonClicked.addListener', 0); |
126 wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0); | 164 wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0); |
127 wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0); | 165 wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0); |
128 wrapper.instrumentChromeApiFunction('omnibox.onInputEntered.addListener', 0); | 166 wrapper.instrumentChromeApiFunction('omnibox.onInputEntered.addListener', 0); |
129 wrapper.instrumentChromeApiFunction( | 167 wrapper.instrumentChromeApiFunction( |
130 'preferencesPrivate.googleGeolocationAccessEnabled.get', | 168 'preferencesPrivate.googleGeolocationAccessEnabled.get', |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
190 chrome.metricsPrivate.recordValue(metricDescription, event); | 228 chrome.metricsPrivate.recordValue(metricDescription, event); |
191 } | 229 } |
192 | 230 |
193 /** | 231 /** |
194 * Adds authorization behavior to the request. | 232 * Adds authorization behavior to the request. |
195 * @param {XMLHttpRequest} request Server request. | 233 * @param {XMLHttpRequest} request Server request. |
196 * @param {function(boolean)} callbackBoolean Completion callback with 'success' | 234 * @param {function(boolean)} callbackBoolean Completion callback with 'success' |
197 * parameter. | 235 * parameter. |
198 */ | 236 */ |
199 function setAuthorization(request, callbackBoolean) { | 237 function setAuthorization(request, callbackBoolean) { |
200 tasks.debugSetStepName('setAuthorization-isSignedIn'); | |
201 authenticationManager.isSignedIn(function(token) { | 238 authenticationManager.isSignedIn(function(token) { |
202 if (!token) { | 239 if (!token) { |
203 callbackBoolean(false); | 240 callbackBoolean(false); |
204 return; | 241 return; |
205 } | 242 } |
206 | 243 |
207 request.setRequestHeader('Authorization', 'Bearer ' + token); | 244 request.setRequestHeader('Authorization', 'Bearer ' + token); |
208 | 245 |
209 // Instrument onloadend to remove stale auth tokens. | 246 // Instrument onloadend to remove stale auth tokens. |
210 var originalOnLoadEnd = request.onloadend; | 247 var originalOnLoadEnd = request.onloadend; |
211 request.onloadend = wrapper.wrapCallback(function(event) { | 248 request.onloadend = wrapper.wrapCallback(function(event) { |
212 if (request.status == HTTP_FORBIDDEN || | 249 if (request.status == HTTP_FORBIDDEN || |
213 request.status == HTTP_UNAUTHORIZED) { | 250 request.status == HTTP_UNAUTHORIZED) { |
214 tasks.debugSetStepName('setAuthorization-removeToken'); | |
215 authenticationManager.removeToken(token, function() { | 251 authenticationManager.removeToken(token, function() { |
216 originalOnLoadEnd(event); | 252 originalOnLoadEnd(event); |
217 }); | 253 }); |
218 } else { | 254 } else { |
219 originalOnLoadEnd(event); | 255 originalOnLoadEnd(event); |
220 } | 256 } |
221 }); | 257 }); |
222 | 258 |
223 callbackBoolean(true); | 259 callbackBoolean(true); |
224 }); | 260 }); |
225 } | 261 } |
226 | 262 |
227 /** | 263 /** |
228 * Parses JSON response from the notification server, show notifications and | 264 * Shows parsed and merged cards as notifications. |
229 * schedule next update. | 265 * @param {Object.<string, MergedCard>} cards Map from chromeNotificationId to |
230 * @param {string} response Server response. | 266 * the merged card, containing cards to show. |
231 * @param {function()} callback Completion callback. | |
232 */ | 267 */ |
233 function parseAndShowNotificationCards(response, callback) { | 268 function showNotificationCards(cards) { |
234 console.log('parseAndShowNotificationCards ' + response); | 269 console.log('showNotificationCards ' + JSON.stringify(cards)); |
235 try { | |
236 var parsedResponse = JSON.parse(response); | |
237 } catch (error) { | |
238 console.error('parseAndShowNotificationCards parse error: ' + error); | |
239 callback(); | |
240 return; | |
241 } | |
242 | 270 |
243 var cards = parsedResponse.cards; | |
244 | |
245 if (!(cards instanceof Array)) { | |
246 callback(); | |
247 return; | |
248 } | |
249 | |
250 if (typeof parsedResponse.next_poll_seconds != 'number') { | |
251 callback(); | |
252 return; | |
253 } | |
254 | |
255 tasks.debugSetStepName('parseAndShowNotificationCards-storage-get'); | |
256 instrumented.storage.local.get(['notificationsData', 'recentDismissals'], | 271 instrumented.storage.local.get(['notificationsData', 'recentDismissals'], |
257 function(items) { | 272 function(items) { |
258 console.log('parseAndShowNotificationCards-get ' + | 273 console.log('showNotificationCards-get ' + |
259 JSON.stringify(items)); | 274 JSON.stringify(items)); |
260 items.notificationsData = items.notificationsData || {}; | 275 items.notificationsData = items.notificationsData || {}; |
261 items.recentDismissals = items.recentDismissals || {}; | 276 items.recentDismissals = items.recentDismissals || {}; |
262 | 277 |
263 tasks.debugSetStepName( | |
264 'parseAndShowNotificationCards-notifications-getAll'); | |
265 instrumented.notifications.getAll(function(notifications) { | 278 instrumented.notifications.getAll(function(notifications) { |
266 console.log('parseAndShowNotificationCards-getAll ' + | 279 console.log('showNotificationCards-getAll ' + |
267 JSON.stringify(notifications)); | 280 JSON.stringify(notifications)); |
268 // TODO(vadimt): Figure out what to do when notifications are | 281 // TODO(vadimt): Figure out what to do when notifications are |
269 // disabled for our extension. | 282 // disabled for our extension. |
270 notifications = notifications || {}; | 283 notifications = notifications || {}; |
271 | 284 |
272 // Build a set of non-expired recent dismissals. It will be used for | 285 // Build a set of non-expired recent dismissals. It will be used for |
273 // client-side filtering of cards. | 286 // client-side filtering of cards. |
274 var updatedRecentDismissals = {}; | 287 var updatedRecentDismissals = {}; |
275 var currentTimeMs = Date.now(); | 288 var currentTimeMs = Date.now(); |
276 for (var notificationId in items.recentDismissals) { | 289 for (var chromeNotificationId in items.recentDismissals) { |
277 if (currentTimeMs - items.recentDismissals[notificationId] < | 290 if (currentTimeMs - items.recentDismissals[chromeNotificationId] < |
278 DISMISS_RETENTION_TIME_MS) { | 291 DISMISS_RETENTION_TIME_MS) { |
279 updatedRecentDismissals[notificationId] = | 292 updatedRecentDismissals[chromeNotificationId] = |
280 items.recentDismissals[notificationId]; | 293 items.recentDismissals[chromeNotificationId]; |
281 } | 294 delete cards[chromeNotificationId]; |
282 } | |
283 | |
284 // Mark existing notifications that received an update in this server | |
285 // response. | |
286 var updatedNotifications = {}; | |
287 | |
288 for (var i = 0; i < cards.length; ++i) { | |
289 var notificationId = cards[i].notificationId; | |
290 if (!(notificationId in updatedRecentDismissals) && | |
291 notificationId in notifications) { | |
292 updatedNotifications[notificationId] = true; | |
293 } | 295 } |
294 } | 296 } |
295 | 297 |
296 // Delete notifications that didn't receive an update. | 298 // Delete notifications that didn't receive an update. |
297 for (var notificationId in notifications) { | 299 for (var chromeNotificationId in notifications) { |
298 console.log('parseAndShowNotificationCards-delete-check ' + | 300 console.log('showNotificationCards-delete-check ' + |
299 notificationId); | 301 chromeNotificationId); |
300 if (!(notificationId in updatedNotifications)) { | 302 if (!(chromeNotificationId in cards)) { |
301 console.log('parseAndShowNotificationCards-delete ' + | 303 console.log( |
302 notificationId); | 304 'showNotificationCards-delete ' + chromeNotificationId); |
303 cardSet.clear(notificationId); | 305 cardSet.clear(chromeNotificationId); |
304 } | 306 } |
305 } | 307 } |
306 | 308 |
307 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); | |
308 | |
309 // Create/update notifications and store their new properties. | 309 // Create/update notifications and store their new properties. |
310 var newNotificationsData = {}; | 310 var newNotificationsData = {}; |
311 for (var i = 0; i < cards.length; ++i) { | 311 for (var chromeNotificationId in cards) { |
312 var card = cards[i]; | 312 var notificationData = |
313 if (!(card.notificationId in updatedRecentDismissals)) { | 313 items.notificationsData[chromeNotificationId]; |
314 var notificationData = | 314 var previousVersion = notifications[chromeNotificationId] && |
315 items.notificationsData[card.notificationId]; | 315 notificationData && |
316 var previousVersion = notifications[card.notificationId] && | 316 notificationData.cardCreateInfo && |
317 notificationData && | 317 notificationData.cardCreateInfo.version; |
318 notificationData.cardCreateInfo && | 318 newNotificationsData[chromeNotificationId] = cardSet.update( |
319 notificationData.cardCreateInfo.version; | 319 chromeNotificationId, |
320 newNotificationsData[card.notificationId] = | 320 cards[chromeNotificationId], |
321 cardSet.update(card, previousVersion); | 321 previousVersion); |
322 } | |
323 } | 322 } |
324 | 323 |
325 updateCardsAttempts.start(parsedResponse.next_poll_seconds); | 324 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); |
326 | 325 |
327 chrome.storage.local.set({ | 326 chrome.storage.local.set({ |
328 notificationsData: newNotificationsData, | 327 notificationsData: newNotificationsData, |
329 recentDismissals: updatedRecentDismissals | 328 recentDismissals: updatedRecentDismissals |
330 }); | 329 }); |
331 callback(); | |
332 }); | 330 }); |
333 }); | 331 }); |
334 } | 332 } |
335 | 333 |
336 /** | 334 /** |
337 * Removes all cards and card state on Google Now close down. | 335 * Removes all cards and card state on Google Now close down. |
338 * For example, this occurs when the geolocation preference is unchecked in the | 336 * For example, this occurs when the geolocation preference is unchecked in the |
339 * content settings. | 337 * content settings. |
340 */ | 338 */ |
341 function removeAllCards() { | 339 function removeAllCards() { |
342 console.log('removeAllCards'); | 340 console.log('removeAllCards'); |
343 | 341 |
344 // TODO(robliao): Once Google Now clears its own checkbox in the | 342 // TODO(robliao): Once Google Now clears its own checkbox in the |
345 // notifications center and bug 260376 is fixed, the below clearing | 343 // notifications center and bug 260376 is fixed, the below clearing |
346 // code is no longer necessary. | 344 // code is no longer necessary. |
347 instrumented.notifications.getAll(function(notifications) { | 345 instrumented.notifications.getAll(function(notifications) { |
348 notifications = notifications || {}; | 346 notifications = notifications || {}; |
349 for (var notificationId in notifications) { | 347 for (var chromeNotificationId in notifications) { |
350 chrome.notifications.clear(notificationId, function() {}); | 348 instrumented.notifications.clear(chromeNotificationId, function() {}); |
351 } | 349 } |
352 chrome.storage.local.set({notificationsData: {}}); | 350 chrome.storage.local.remove(['notificationsData', 'notificationGroups']); |
| 351 }); |
| 352 } |
| 353 |
| 354 /** |
| 355 * Merges an unmerged notification into a merged card with same ID. |
| 356 * @param {MergedCard=} mergedCard Existing merged card or undefined if a merged |
| 357 * card doesn't exist (i.e. we see this ID for the first time while |
| 358 * merging). |
| 359 * @param {UnmergedNotification} unmergedNotification Notification as it was |
| 360 * received from the server. |
| 361 * @param {number} notificationTimestamp The moment the unmerged card was |
| 362 * received. |
| 363 * @param {number} notificationGroupRank Rank of the group of the unmerged card. |
| 364 * @return {MergedCard} Result of merging |unmergedNotification| into |
| 365 * |mergedCard|. |
| 366 */ |
| 367 function mergeCards( |
| 368 mergedCard, |
| 369 unmergedNotification, |
| 370 notificationTimestamp, |
| 371 notificationGroupRank) { |
| 372 var result = mergedCard || {dismissals: []}; |
| 373 |
| 374 var priority = mergedCard ? |
| 375 Math.max( |
| 376 mergedCard.notification.priority, |
| 377 unmergedNotification.chromeNotificationOptions.priority) : |
| 378 unmergedNotification.chromeNotificationOptions.priority; |
| 379 |
| 380 if (!mergedCard || notificationGroupRank > mergedCard.groupRank) { |
| 381 result.groupRank = notificationGroupRank; |
| 382 var showTime = unmergedNotification.trigger && |
| 383 unmergedNotification.trigger.showTimeSec && |
| 384 (notificationTimestamp + |
| 385 unmergedNotification.trigger.showTimeSec * MS_IN_SECOND); |
| 386 var hideTime = unmergedNotification.trigger && |
| 387 unmergedNotification.trigger.hideTimeSec && |
| 388 (notificationTimestamp + |
| 389 unmergedNotification.trigger.hideTimeSec * MS_IN_SECOND); |
| 390 result.trigger = { |
| 391 showTime: showTime, |
| 392 hideTime: hideTime |
| 393 }; |
| 394 } |
| 395 |
| 396 if (!mergedCard || notificationTimestamp > mergedCard.timestamp) { |
| 397 result.timestamp = notificationTimestamp; |
| 398 result.notification = unmergedNotification.chromeNotificationOptions; |
| 399 result.actionUrls = unmergedNotification.actionUrls; |
| 400 result.version = unmergedNotification.version; |
| 401 } |
| 402 |
| 403 result.notification.priority = priority; |
| 404 var dismissalData = { |
| 405 notificationId: unmergedNotification.notificationId, |
| 406 parameters: unmergedNotification.dismissal |
| 407 }; |
| 408 result.dismissals.push(dismissalData); |
| 409 |
| 410 return result; |
| 411 } |
| 412 |
| 413 /** |
| 414 * Merges a card group into a set of merged cards. |
| 415 * @param {Object.<string, MergedCard>} mergedCards Map from |
| 416 * chromeNotificationId to a merged card. |
| 417 * This is an input/output parameter. |
| 418 * @param {StorageGroup} storageGroup Group to merge into the merged card set. |
| 419 */ |
| 420 function mergeGroup(mergedCards, storageGroup) { |
| 421 for (var i = 0; i < storageGroup.cards.length; i++) { |
| 422 var card = storageGroup.cards[i]; |
| 423 mergedCards[card.chromeNotificationId] = mergeCards( |
| 424 mergedCards[card.chromeNotificationId], |
| 425 card, |
| 426 storageGroup.cardsTimestamp, |
| 427 storageGroup.rank); |
| 428 } |
| 429 } |
| 430 |
| 431 /** |
| 432 * Schedules next cards poll. |
| 433 * @param {Object.<string, StorageGroup>} groups Map from group name to group |
| 434 * information. |
| 435 */ |
| 436 function scheduleNextPoll(groups) { |
| 437 var nextPollTime = null; |
| 438 |
| 439 for (var groupName in groups) { |
| 440 var group = groups[groupName]; |
| 441 nextPollTime = nextPollTime == null ? |
| 442 group.nextPollTime : Math.min(group.nextPollTime, nextPollTime); |
| 443 } |
| 444 |
| 445 verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null'); |
| 446 |
| 447 var nextPollDelaySeconds = Math.max( |
| 448 (nextPollTime - Date.now()) / MS_IN_SECOND, |
| 449 MINIMUM_POLLING_PERIOD_SECONDS); |
| 450 updateCardsAttempts.start(nextPollDelaySeconds); |
| 451 } |
| 452 |
| 453 /** |
| 454 * Parses JSON response from the notification server, shows notifications and |
| 455 * schedules next update. |
| 456 * @param {string} response Server response. |
| 457 */ |
| 458 function parseAndShowNotificationCards(response) { |
| 459 console.log('parseAndShowNotificationCards ' + response); |
| 460 var parsedResponse = JSON.parse(response); |
| 461 |
| 462 var groups = parsedResponse.groups; |
| 463 |
| 464 // Populate groups with corresponding cards. |
| 465 if (parsedResponse.notifications) { |
| 466 for (var i = 0; i != parsedResponse.notifications.length; ++i) { |
| 467 var card = parsedResponse.notifications[i]; |
| 468 var group = groups[card.groupName]; |
| 469 group.cards = group.cards || []; |
| 470 group.cards.push(card); |
| 471 } |
| 472 } |
| 473 |
| 474 instrumented.storage.local.get('notificationGroups', function(items) { |
| 475 console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items)); |
| 476 items.notificationGroups = items.notificationGroups || {}; |
| 477 |
| 478 var now = Date.now(); |
| 479 |
| 480 // Build updated set of groups and merge cards from all groups into one set. |
| 481 var updatedGroups = {}; |
| 482 var mergedCards = {}; |
| 483 |
| 484 for (var groupName in groups) { |
| 485 var receivedGroup = groups[groupName]; |
| 486 var storageGroup = items.notificationGroups[groupName] || { |
| 487 cards: [], |
| 488 cardsTimestamp: undefined, |
| 489 nextPollTime: now, |
| 490 rank: undefined |
| 491 }; |
| 492 |
| 493 if (receivedGroup.requested) |
| 494 receivedGroup.cards = receivedGroup.cards || []; |
| 495 |
| 496 if (receivedGroup.cards) { |
| 497 storageGroup.cards = receivedGroup.cards; |
| 498 storageGroup.cardsTimestamp = now; |
| 499 storageGroup.rank = receivedGroup.rank; |
| 500 } |
| 501 |
| 502 if (receivedGroup.nextPollSeconds !== undefined) { |
| 503 storageGroup.nextPollTime = |
| 504 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; |
| 505 } |
| 506 |
| 507 updatedGroups[groupName] = storageGroup; |
| 508 |
| 509 mergeGroup(mergedCards, storageGroup); |
| 510 } |
| 511 |
| 512 scheduleNextPoll(updatedGroups); |
| 513 |
| 514 chrome.storage.local.set({notificationGroups: updatedGroups}); |
| 515 |
| 516 showNotificationCards(mergedCards); |
353 }); | 517 }); |
354 } | 518 } |
355 | 519 |
356 /** | 520 /** |
357 * Requests notification cards from the server. | 521 * Requests notification cards from the server. |
358 * @param {Location} position Location of this computer. | 522 * @param {Location} position Location of this computer. |
359 * @param {function()} callback Completion callback. | 523 */ |
360 */ | 524 function requestNotificationCards(position) { |
361 function requestNotificationCards(position, callback) { | |
362 console.log('requestNotificationCards ' + JSON.stringify(position) + | 525 console.log('requestNotificationCards ' + JSON.stringify(position) + |
363 ' from ' + NOTIFICATION_CARDS_URL); | 526 ' from ' + NOTIFICATION_CARDS_URL); |
364 | 527 |
365 if (!NOTIFICATION_CARDS_URL) { | 528 if (!NOTIFICATION_CARDS_URL) |
366 callback(); | |
367 return; | 529 return; |
368 } | |
369 | 530 |
370 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL); | 531 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL); |
371 | 532 |
372 // TODO(vadimt): Should we use 'q' as the parameter name? | 533 instrumented.storage.local.get('notificationGroups', function(items) { |
373 var requestParameters = | 534 console.log('requestNotificationCards-storage-get ' + |
374 'q=' + position.coords.latitude + | 535 JSON.stringify(items)); |
375 ',' + position.coords.longitude + | 536 |
376 ',' + position.coords.accuracy; | 537 var requestParameters = '?timeZoneOffsetMs=' + |
377 | 538 (-new Date().getTimezoneOffset() * MS_IN_MINUTE); |
378 var request = buildServerRequest('notifications', | 539 |
379 'application/x-www-form-urlencoded'); | 540 if (items.notificationGroups) { |
380 | 541 var now = Date.now(); |
381 request.onloadend = function(event) { | 542 |
382 console.log('requestNotificationCards-onloadend ' + request.status); | 543 for (var groupName in items.notificationGroups) { |
383 if (request.status == HTTP_OK) { | 544 var group = items.notificationGroups[groupName]; |
384 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); | 545 if (group.nextPollTime <= now) |
385 parseAndShowNotificationCards(request.response, callback); | 546 requestParameters += ('&requestTypes=' + groupName); |
386 } else { | 547 } |
387 callback(); | 548 } |
388 } | 549 |
389 }; | 550 console.log('requestNotificationCards: request=' + requestParameters); |
390 | 551 |
391 setAuthorization(request, function(success) { | 552 var request = buildServerRequest('GET', |
392 if (success) { | 553 'notifications' + requestParameters); |
393 tasks.debugSetStepName('requestNotificationCards-send-request'); | 554 |
394 request.send(requestParameters); | 555 request.onloadend = function(event) { |
395 } else { | 556 console.log('requestNotificationCards-onloadend ' + request.status); |
396 callback(); | 557 if (request.status == HTTP_OK) { |
397 } | 558 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); |
| 559 parseAndShowNotificationCards(request.response); |
| 560 } |
| 561 }; |
| 562 |
| 563 setAuthorization(request, function(success) { |
| 564 if (success) |
| 565 request.send(); |
| 566 }); |
398 }); | 567 }); |
399 } | 568 } |
400 | 569 |
401 /** | 570 /** |
402 * Starts getting location for a cards update. | 571 * Starts getting location for a cards update. |
403 */ | 572 */ |
404 function requestLocation() { | 573 function requestLocation() { |
405 console.log('requestLocation'); | 574 console.log('requestLocation'); |
406 recordEvent(GoogleNowEvent.LOCATION_REQUEST); | 575 recordEvent(GoogleNowEvent.LOCATION_REQUEST); |
407 // TODO(vadimt): Figure out location request options. | 576 // TODO(vadimt): Figure out location request options. |
(...skipping 21 matching lines...) Expand all Loading... |
429 } | 598 } |
430 | 599 |
431 /** | 600 /** |
432 * Obtains new location; requests and shows notification cards based on this | 601 * Obtains new location; requests and shows notification cards based on this |
433 * location. | 602 * location. |
434 * @param {Location} position Location of this computer. | 603 * @param {Location} position Location of this computer. |
435 */ | 604 */ |
436 function updateNotificationsCards(position) { | 605 function updateNotificationsCards(position) { |
437 console.log('updateNotificationsCards ' + JSON.stringify(position) + | 606 console.log('updateNotificationsCards ' + JSON.stringify(position) + |
438 ' @' + new Date()); | 607 ' @' + new Date()); |
439 tasks.add(UPDATE_CARDS_TASK_NAME, function(callback) { | 608 tasks.add(UPDATE_CARDS_TASK_NAME, function() { |
440 console.log('updateNotificationsCards-task-begin'); | 609 console.log('updateNotificationsCards-task-begin'); |
441 updateCardsAttempts.isRunning(function(running) { | 610 updateCardsAttempts.isRunning(function(running) { |
442 if (running) { | 611 if (running) { |
443 updateCardsAttempts.planForNext(function() { | 612 updateCardsAttempts.planForNext(function() { |
444 processPendingDismissals(function(success) { | 613 processPendingDismissals(function(success) { |
445 if (success) { | 614 if (success) { |
446 // The cards are requested only if there are no unsent dismissals. | 615 // The cards are requested only if there are no unsent dismissals. |
447 requestNotificationCards(position, callback); | 616 requestNotificationCards(position); |
448 } else { | |
449 callback(); | |
450 } | 617 } |
451 }); | 618 }); |
452 }); | 619 }); |
453 } | 620 } |
454 }); | 621 }); |
455 }); | 622 }); |
456 } | 623 } |
457 | 624 |
458 /** | 625 /** |
459 * Sends a server request to dismiss a card. | 626 * Sends a server request to dismiss a card. |
460 * @param {string} notificationId Unique identifier of the card. | 627 * @param {string} chromeNotificationId chrome.notifications ID of the card. |
461 * @param {number} dismissalTimeMs Time of the user's dismissal of the card in | 628 * @param {number} dismissalTimeMs Time of the user's dismissal of the card in |
462 * milliseconds since epoch. | 629 * milliseconds since epoch. |
463 * @param {Object} dismissalParameters Dismissal parameters. | 630 * @param {DismissalData} dismissalData Data to build a dismissal request. |
464 * @param {function(boolean)} callbackBoolean Completion callback with 'done' | 631 * @param {function(boolean)} callbackBoolean Completion callback with 'done' |
465 * parameter. | 632 * parameter. |
466 */ | 633 */ |
467 function requestCardDismissal( | 634 function requestCardDismissal( |
468 notificationId, dismissalTimeMs, dismissalParameters, callbackBoolean) { | 635 chromeNotificationId, dismissalTimeMs, dismissalData, callbackBoolean) { |
469 console.log('requestDismissingCard ' + notificationId + ' from ' + | 636 console.log('requestDismissingCard ' + chromeNotificationId + ' from ' + |
470 NOTIFICATION_CARDS_URL); | 637 NOTIFICATION_CARDS_URL); |
471 | 638 |
472 var dismissalAge = Date.now() - dismissalTimeMs; | 639 var dismissalAge = Date.now() - dismissalTimeMs; |
473 | 640 |
474 if (dismissalAge > MAXIMUM_DISMISSAL_AGE_MS) { | 641 if (dismissalAge > MAXIMUM_DISMISSAL_AGE_MS) { |
475 callbackBoolean(true); | 642 callbackBoolean(true); |
476 return; | 643 return; |
477 } | 644 } |
478 | 645 |
479 recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL); | 646 recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL); |
480 var request = buildServerRequest('dismiss', 'application/json'); | 647 |
| 648 var request = 'notifications/' + dismissalData.notificationId + |
| 649 '?age=' + dismissalAge + |
| 650 '&chromeNotificationId=' + chromeNotificationId; |
| 651 |
| 652 for (var paramField in dismissalData.parameters) |
| 653 request += ('&' + paramField + '=' + dismissalData.parameters[paramField]); |
| 654 |
| 655 console.log('requestCardDismissal: request=' + request); |
| 656 |
| 657 var request = buildServerRequest('DELETE', request); |
481 request.onloadend = function(event) { | 658 request.onloadend = function(event) { |
482 console.log('requestDismissingCard-onloadend ' + request.status); | 659 console.log('requestDismissingCard-onloadend ' + request.status); |
483 if (request.status == HTTP_OK) | 660 if (request.status == HTTP_NOCONTENT) |
484 recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS); | 661 recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS); |
485 | 662 |
486 // A dismissal doesn't require further retries if it was successful or | 663 // A dismissal doesn't require further retries if it was successful or |
487 // doesn't have a chance for successful completion. | 664 // doesn't have a chance for successful completion. |
488 var done = request.status == HTTP_OK || | 665 var done = request.status == HTTP_NOCONTENT || |
489 request.status == HTTP_BAD_REQUEST || | 666 request.status == HTTP_BAD_REQUEST || |
490 request.status == HTTP_METHOD_NOT_ALLOWED; | 667 request.status == HTTP_METHOD_NOT_ALLOWED; |
491 callbackBoolean(done); | 668 callbackBoolean(done); |
492 }; | 669 }; |
493 | 670 |
494 setAuthorization(request, function(success) { | 671 setAuthorization(request, function(success) { |
495 if (success) { | 672 if (success) |
496 tasks.debugSetStepName('requestCardDismissal-send-request'); | 673 request.send(); |
497 | 674 else |
498 var dismissalObject = { | |
499 id: notificationId, | |
500 age: dismissalAge, | |
501 dismissal: dismissalParameters | |
502 }; | |
503 request.send(JSON.stringify(dismissalObject)); | |
504 } else { | |
505 callbackBoolean(false); | 675 callbackBoolean(false); |
506 } | |
507 }); | 676 }); |
508 } | 677 } |
509 | 678 |
510 /** | 679 /** |
511 * Tries to send dismiss requests for all pending dismissals. | 680 * Tries to send dismiss requests for all pending dismissals. |
512 * @param {function(boolean)} callbackBoolean Completion callback with 'success' | 681 * @param {function(boolean)} callbackBoolean Completion callback with 'success' |
513 * parameter. Success means that no pending dismissals are left. | 682 * parameter. Success means that no pending dismissals are left. |
514 */ | 683 */ |
515 function processPendingDismissals(callbackBoolean) { | 684 function processPendingDismissals(callbackBoolean) { |
516 tasks.debugSetStepName('processPendingDismissals-storage-get'); | |
517 instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'], | 685 instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'], |
518 function(items) { | 686 function(items) { |
519 console.log('processPendingDismissals-storage-get ' + | 687 console.log('processPendingDismissals-storage-get ' + |
520 JSON.stringify(items)); | 688 JSON.stringify(items)); |
521 items.pendingDismissals = items.pendingDismissals || []; | 689 items.pendingDismissals = items.pendingDismissals || []; |
522 items.recentDismissals = items.recentDismissals || {}; | 690 items.recentDismissals = items.recentDismissals || {}; |
523 | 691 |
524 var dismissalsChanged = false; | 692 var dismissalsChanged = false; |
525 | 693 |
526 function onFinish(success) { | 694 function onFinish(success) { |
(...skipping 10 matching lines...) Expand all Loading... |
537 if (items.pendingDismissals.length == 0) { | 705 if (items.pendingDismissals.length == 0) { |
538 dismissalAttempts.stop(); | 706 dismissalAttempts.stop(); |
539 onFinish(true); | 707 onFinish(true); |
540 return; | 708 return; |
541 } | 709 } |
542 | 710 |
543 // Send dismissal for the first card, and if successful, repeat | 711 // Send dismissal for the first card, and if successful, repeat |
544 // recursively with the rest. | 712 // recursively with the rest. |
545 var dismissal = items.pendingDismissals[0]; | 713 var dismissal = items.pendingDismissals[0]; |
546 requestCardDismissal( | 714 requestCardDismissal( |
547 dismissal.notificationId, | 715 dismissal.chromeNotificationId, |
548 dismissal.time, | 716 dismissal.time, |
549 dismissal.parameters, | 717 dismissal.dismissalData, |
550 function(done) { | 718 function(done) { |
551 if (done) { | 719 if (done) { |
552 dismissalsChanged = true; | 720 dismissalsChanged = true; |
553 items.pendingDismissals.splice(0, 1); | 721 items.pendingDismissals.splice(0, 1); |
554 items.recentDismissals[dismissal.notificationId] = Date.now(); | 722 items.recentDismissals[dismissal.chromeNotificationId] = |
| 723 Date.now(); |
555 doProcessDismissals(); | 724 doProcessDismissals(); |
556 } else { | 725 } else { |
557 onFinish(false); | 726 onFinish(false); |
558 } | 727 } |
559 }); | 728 }); |
560 } | 729 } |
561 | 730 |
562 doProcessDismissals(); | 731 doProcessDismissals(); |
563 }); | 732 }); |
564 } | 733 } |
565 | 734 |
566 /** | 735 /** |
567 * Submits a task to send pending dismissals. | 736 * Submits a task to send pending dismissals. |
568 */ | 737 */ |
569 function retryPendingDismissals() { | 738 function retryPendingDismissals() { |
570 tasks.add(RETRY_DISMISS_TASK_NAME, function(callback) { | 739 tasks.add(RETRY_DISMISS_TASK_NAME, function() { |
571 dismissalAttempts.planForNext(function() { | 740 dismissalAttempts.planForNext(function() { |
572 processPendingDismissals(function(success) { callback(); }); | 741 processPendingDismissals(function(success) {}); |
573 }); | 742 }); |
574 }); | 743 }); |
575 } | 744 } |
576 | 745 |
577 /** | 746 /** |
578 * Opens URL corresponding to the clicked part of the notification. | 747 * Opens URL corresponding to the clicked part of the notification. |
579 * @param {string} notificationId Unique identifier of the notification. | 748 * @param {string} chromeNotificationId chrome.notifications ID of the card. |
580 * @param {function(Object): string} selector Function that extracts the url for | 749 * @param {function(Object): string} selector Function that extracts the url for |
581 * the clicked area from the button action URLs info. | 750 * the clicked area from the button action URLs info. |
582 */ | 751 */ |
583 function onNotificationClicked(notificationId, selector) { | 752 function onNotificationClicked(chromeNotificationId, selector) { |
584 instrumented.storage.local.get('notificationsData', function(items) { | 753 instrumented.storage.local.get('notificationsData', function(items) { |
585 var notificationData = items && | 754 var notificationData = items && |
586 items.notificationsData && | 755 items.notificationsData && |
587 items.notificationsData[notificationId]; | 756 items.notificationsData[chromeNotificationId]; |
588 | 757 |
589 if (!notificationData) | 758 if (!notificationData) |
590 return; | 759 return; |
591 | 760 |
592 var actionUrls = notificationData.actionUrls; | 761 var url = selector(notificationData.actionUrls); |
593 if (typeof actionUrls != 'object') { | |
594 return; | |
595 } | |
596 | |
597 var url = selector(actionUrls); | |
598 if (!url) | 762 if (!url) |
599 return; | 763 return; |
600 | 764 |
601 instrumented.tabs.create({url: url}, function(tab) { | 765 instrumented.tabs.create({url: url}, function(tab) { |
602 if (tab) | 766 if (tab) |
603 chrome.windows.update(tab.windowId, {focused: true}); | 767 chrome.windows.update(tab.windowId, {focused: true}); |
604 else | 768 else |
605 chrome.windows.create({url: url, focused: true}); | 769 chrome.windows.create({url: url, focused: true}); |
606 }); | 770 }); |
607 }); | 771 }); |
(...skipping 12 matching lines...) Expand all Loading... |
620 // The googlegeolocationaccessenabled preference change callback | 784 // The googlegeolocationaccessenabled preference change callback |
621 // will take care of starting the poll for cards. | 785 // will take care of starting the poll for cards. |
622 } else { | 786 } else { |
623 chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastClickedNo'); | 787 chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastClickedNo'); |
624 onStateChange(); | 788 onStateChange(); |
625 } | 789 } |
626 } | 790 } |
627 | 791 |
628 /** | 792 /** |
629 * Callback for chrome.notifications.onClosed event. | 793 * Callback for chrome.notifications.onClosed event. |
630 * @param {string} notificationId Unique identifier of the notification. | 794 * @param {string} chromeNotificationId chrome.notifications ID of the card. |
631 * @param {boolean} byUser Whether the notification was closed by the user. | 795 * @param {boolean} byUser Whether the notification was closed by the user. |
632 */ | 796 */ |
633 function onNotificationClosed(notificationId, byUser) { | 797 function onNotificationClosed(chromeNotificationId, byUser) { |
634 if (!byUser) | 798 if (!byUser) |
635 return; | 799 return; |
636 | 800 |
637 if (notificationId == WELCOME_TOAST_NOTIFICATION_ID) { | 801 if (chromeNotificationId == WELCOME_TOAST_NOTIFICATION_ID) { |
638 // Even though they only closed the notification without clicking no, treat | 802 // Even though they only closed the notification without clicking no, treat |
639 // it as though they clicked No anwyay, and don't show the toast again. | 803 // it as though they clicked No anwyay, and don't show the toast again. |
640 chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastDismissed'); | 804 chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastDismissed'); |
641 chrome.storage.local.set({userRespondedToToast: true}); | 805 chrome.storage.local.set({userRespondedToToast: true}); |
642 return; | 806 return; |
643 } | 807 } |
644 | 808 |
645 // At this point we are guaranteed that the notification is a now card. | 809 // At this point we are guaranteed that the notification is a now card. |
646 chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed'); | 810 chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed'); |
647 | 811 |
648 tasks.add(DISMISS_CARD_TASK_NAME, function(callback) { | 812 tasks.add(DISMISS_CARD_TASK_NAME, function() { |
649 dismissalAttempts.start(); | 813 dismissalAttempts.start(); |
650 | 814 |
651 // Deleting the notification in case it was re-added while this task was | 815 // Deleting the notification in case it was re-added while this task was |
652 // scheduled, waiting for execution. | 816 // scheduled, waiting for execution. |
653 cardSet.clear(notificationId); | 817 cardSet.clear(chromeNotificationId); |
654 | 818 |
655 tasks.debugSetStepName('onNotificationClosed-storage-get'); | 819 instrumented.storage.local.get( |
656 instrumented.storage.local.get(['pendingDismissals', 'notificationsData'], | 820 ['pendingDismissals', 'notificationsData'], function(items) { |
657 function(items) { | 821 items.pendingDismissals = items.pendingDismissals || []; |
658 items.pendingDismissals = items.pendingDismissals || []; | 822 items.notificationsData = items.notificationsData || {}; |
659 items.notificationsData = items.notificationsData || {}; | |
660 | 823 |
661 var notificationData = items.notificationsData[notificationId]; | 824 var notificationData = items.notificationsData[chromeNotificationId]; |
662 | 825 |
| 826 if (notificationData && notificationData.dismissals) { |
| 827 for (var i = 0; i < notificationData.dismissals.length; i++) { |
663 var dismissal = { | 828 var dismissal = { |
664 notificationId: notificationId, | 829 chromeNotificationId: chromeNotificationId, |
665 time: Date.now(), | 830 time: Date.now(), |
666 parameters: notificationData && notificationData.dismissalParameters | 831 dismissalData: notificationData.dismissals[i] |
667 }; | 832 }; |
668 items.pendingDismissals.push(dismissal); | 833 items.pendingDismissals.push(dismissal); |
669 chrome.storage.local.set( | 834 } |
670 {pendingDismissals: items.pendingDismissals}); | 835 |
671 processPendingDismissals(function(success) { callback(); }); | 836 chrome.storage.local.set({pendingDismissals: items.pendingDismissals}); |
672 }); | 837 } |
| 838 |
| 839 processPendingDismissals(function(success) {}); |
| 840 }); |
673 }); | 841 }); |
674 } | 842 } |
675 | 843 |
676 /** | 844 /** |
677 * Initializes the polling system to start monitoring location and fetching | 845 * Initializes the polling system to start monitoring location and fetching |
678 * cards. | 846 * cards. |
679 */ | 847 */ |
680 function startPollingCards() { | 848 function startPollingCards() { |
681 // Create an update timer for a case when for some reason location request | 849 // Create an update timer for a case when for some reason location request |
682 // gets stuck. | 850 // gets stuck. |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
909 var buttons = [{title: 'Yes'}, {title: 'No'}]; | 1077 var buttons = [{title: 'Yes'}, {title: 'No'}]; |
910 var options = { | 1078 var options = { |
911 type: 'basic', | 1079 type: 'basic', |
912 title: 'Enable Google Now Cards', | 1080 title: 'Enable Google Now Cards', |
913 message: 'Would you like to be shown Google Now cards?', | 1081 message: 'Would you like to be shown Google Now cards?', |
914 iconUrl: 'http://www.gstatic.com/googlenow/chrome/default.png', | 1082 iconUrl: 'http://www.gstatic.com/googlenow/chrome/default.png', |
915 priority: 2, | 1083 priority: 2, |
916 buttons: buttons | 1084 buttons: buttons |
917 }; | 1085 }; |
918 instrumented.notifications.create(WELCOME_TOAST_NOTIFICATION_ID, options, | 1086 instrumented.notifications.create(WELCOME_TOAST_NOTIFICATION_ID, options, |
919 function(notificationId) {}); | 1087 function(chromeNotificationId) {}); |
920 } | 1088 } |
921 | 1089 |
922 /** | 1090 /** |
923 * Hides the welcome toast. | 1091 * Hides the welcome toast. |
924 */ | 1092 */ |
925 function hideWelcomeToast() { | 1093 function hideWelcomeToast() { |
926 chrome.notifications.clear( | 1094 instrumented.notifications.clear( |
927 WELCOME_TOAST_NOTIFICATION_ID, | 1095 WELCOME_TOAST_NOTIFICATION_ID, |
928 function() {}); | 1096 function() {}); |
929 } | 1097 } |
930 | 1098 |
931 instrumented.runtime.onInstalled.addListener(function(details) { | 1099 instrumented.runtime.onInstalled.addListener(function(details) { |
932 console.log('onInstalled ' + JSON.stringify(details)); | 1100 console.log('onInstalled ' + JSON.stringify(details)); |
933 if (details.reason != 'chrome_update') { | 1101 if (details.reason != 'chrome_update') { |
934 initialize(); | 1102 initialize(); |
935 } | 1103 } |
936 }); | 1104 }); |
(...skipping 12 matching lines...) Expand all Loading... |
949 prefValue.value); | 1117 prefValue.value); |
950 onStateChange(); | 1118 onStateChange(); |
951 }); | 1119 }); |
952 | 1120 |
953 authenticationManager.addListener(function() { | 1121 authenticationManager.addListener(function() { |
954 console.log('signIn State Change'); | 1122 console.log('signIn State Change'); |
955 onStateChange(); | 1123 onStateChange(); |
956 }); | 1124 }); |
957 | 1125 |
958 instrumented.notifications.onClicked.addListener( | 1126 instrumented.notifications.onClicked.addListener( |
959 function(notificationId) { | 1127 function(chromeNotificationId) { |
960 chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked'); | 1128 chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked'); |
961 onNotificationClicked(notificationId, function(actionUrls) { | 1129 onNotificationClicked(chromeNotificationId, function(actionUrls) { |
962 return actionUrls.messageUrl; | 1130 return actionUrls && actionUrls.messageUrl; |
963 }); | 1131 }); |
964 }); | 1132 }); |
965 | 1133 |
966 instrumented.notifications.onButtonClicked.addListener( | 1134 instrumented.notifications.onButtonClicked.addListener( |
967 function(notificationId, buttonIndex) { | 1135 function(chromeNotificationId, buttonIndex) { |
968 if (notificationId == WELCOME_TOAST_NOTIFICATION_ID) { | 1136 if (chromeNotificationId == WELCOME_TOAST_NOTIFICATION_ID) { |
969 onToastNotificationClicked(buttonIndex); | 1137 onToastNotificationClicked(buttonIndex); |
970 } else { | 1138 } else { |
971 chrome.metricsPrivate.recordUserAction( | 1139 chrome.metricsPrivate.recordUserAction( |
972 'GoogleNow.ButtonClicked' + buttonIndex); | 1140 'GoogleNow.ButtonClicked' + buttonIndex); |
973 onNotificationClicked(notificationId, function(actionUrls) { | 1141 onNotificationClicked(chromeNotificationId, function(actionUrls) { |
974 var url = actionUrls.buttonUrls[buttonIndex]; | 1142 var url = actionUrls.buttonUrls[buttonIndex]; |
975 verify(url, 'onButtonClicked: no url for a button'); | 1143 verify(url, 'onButtonClicked: no url for a button'); |
976 return url; | 1144 return url; |
977 }); | 1145 }); |
978 } | 1146 } |
979 }); | 1147 }); |
980 | 1148 |
981 instrumented.notifications.onClosed.addListener(onNotificationClosed); | 1149 instrumented.notifications.onClosed.addListener(onNotificationClosed); |
982 | 1150 |
983 instrumented.location.onLocationUpdate.addListener(function(position) { | 1151 instrumented.location.onLocationUpdate.addListener(function(position) { |
984 recordEvent(GoogleNowEvent.LOCATION_UPDATE); | 1152 recordEvent(GoogleNowEvent.LOCATION_UPDATE); |
985 updateNotificationsCards(position); | 1153 updateNotificationsCards(position); |
986 }); | 1154 }); |
987 | 1155 |
988 instrumented.omnibox.onInputEntered.addListener(function(text) { | 1156 instrumented.omnibox.onInputEntered.addListener(function(text) { |
989 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; | 1157 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; |
990 initialize(); | 1158 initialize(); |
991 }); | 1159 }); |
OLD | NEW |