Chromium Code Reviews| 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 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 95 var UPDATE_CARDS_TASK_NAME = 'update-cards'; | 95 var UPDATE_CARDS_TASK_NAME = 'update-cards'; |
| 96 var DISMISS_CARD_TASK_NAME = 'dismiss-card'; | 96 var DISMISS_CARD_TASK_NAME = 'dismiss-card'; |
| 97 var RETRY_DISMISS_TASK_NAME = 'retry-dismiss'; | 97 var RETRY_DISMISS_TASK_NAME = 'retry-dismiss'; |
| 98 var STATE_CHANGED_TASK_NAME = 'state-changed'; | 98 var STATE_CHANGED_TASK_NAME = 'state-changed'; |
| 99 var SHOW_ON_START_TASK_NAME = 'show-cards-on-start'; | 99 var SHOW_ON_START_TASK_NAME = 'show-cards-on-start'; |
| 100 var ON_PUSH_MESSAGE_START_TASK_NAME = 'on-push-message'; | 100 var ON_PUSH_MESSAGE_START_TASK_NAME = 'on-push-message'; |
| 101 | 101 |
| 102 var LOCATION_WATCH_NAME = 'location-watch'; | 102 var LOCATION_WATCH_NAME = 'location-watch'; |
| 103 | 103 |
| 104 /** | 104 /** |
| 105 * Notification as it's sent by the server. | 105 * Group as received from the server. |
| 106 * | 106 * |
| 107 * @typedef {{ | 107 * @typedef {{ |
| 108 * notificationId: string, | 108 * nextPollSeconds: (string|undefined), |
| 109 * chromeNotificationId: string, | 109 * rank: (number|undefined), |
| 110 * trigger: Object=, | 110 * requested: (boolean|undefined) |
| 111 * version: number, | |
| 112 * chromeNotificationOptions: Object, | |
| 113 * actionUrls: Object=, | |
| 114 * dismissal: Object | |
| 115 * }} | 111 * }} |
| 116 */ | 112 */ |
| 117 var UnmergedNotification; | 113 var ReceivedGroup; |
| 114 | |
| 115 /** | |
| 116 * Server response with notifications and groups. | |
| 117 * | |
| 118 * @typedef {{ | |
| 119 * googleNowDisabled: (boolean|undefined), | |
| 120 * groups: Object.<string, ReceivedGroup>, | |
| 121 * notifications: Array.<ReceivedNotification> | |
| 122 * }} | |
| 123 */ | |
| 124 var ServerResponse; | |
| 118 | 125 |
| 119 /** | 126 /** |
| 120 * Notification group as the client stores it. |cardsTimestamp| and |rank| are | 127 * Notification group as the client stores it. |cardsTimestamp| and |rank| are |
| 121 * defined if |cards| is non-empty. |nextPollTime| is undefined if the server | 128 * defined if |cards| is non-empty. |nextPollTime| is undefined if the server |
| 122 * (1) never sent 'nextPollSeconds' for the group or | 129 * (1) never sent 'nextPollSeconds' for the group or |
| 123 * (2) didn't send 'nextPollSeconds' with the last group update containing a | 130 * (2) didn't send 'nextPollSeconds' with the last group update containing a |
| 124 * cards update and all the times after that. | 131 * cards update and all the times after that. |
| 125 * | 132 * |
| 126 * @typedef {{ | 133 * @typedef {{ |
| 127 * cards: Array.<UnmergedNotification>, | 134 * cards: Array.<ReceivedNotification>, |
| 128 * cardsTimestamp: number=, | 135 * cardsTimestamp: (number|undefined), |
| 129 * nextPollTime: number=, | 136 * nextPollTime: (number|undefined), |
| 130 * rank: number= | 137 * rank: (number|undefined) |
| 131 * }} | 138 * }} |
| 132 */ | 139 */ |
| 133 var StorageGroup; | 140 var StoredNotificationGroup; |
| 141 | |
| 142 /** | |
| 143 * Pending (not yet successfully sent) dismissal for a received notification. | |
| 144 * |time| is the moment when the user requested dismissal. | |
| 145 * | |
| 146 * @typedef {{ | |
| 147 * chromeNotificationId: ChromeNotificationId, | |
| 148 * time: number, | |
| 149 * dismissalData: DismissalData | |
| 150 * }} | |
| 151 */ | |
| 152 var PendingDismissal; | |
| 134 | 153 |
| 135 /** | 154 /** |
| 136 * Checks if a new task can't be scheduled when another task is already | 155 * Checks if a new task can't be scheduled when another task is already |
| 137 * scheduled. | 156 * scheduled. |
| 138 * @param {string} newTaskName Name of the new task. | 157 * @param {string} newTaskName Name of the new task. |
| 139 * @param {string} scheduledTaskName Name of the scheduled task. | 158 * @param {string} scheduledTaskName Name of the scheduled task. |
| 140 * @return {boolean} Whether the new task conflicts with the existing task. | 159 * @return {boolean} Whether the new task conflicts with the existing task. |
| 141 */ | 160 */ |
| 142 function areTasksConflicting(newTaskName, scheduledTaskName) { | 161 function areTasksConflicting(newTaskName, scheduledTaskName) { |
| 143 if (newTaskName == UPDATE_CARDS_TASK_NAME && | 162 if (newTaskName == UPDATE_CARDS_TASK_NAME && |
| (...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 266 } else { | 285 } else { |
| 267 originalOnLoadEnd(event); | 286 originalOnLoadEnd(event); |
| 268 } | 287 } |
| 269 }); | 288 }); |
| 270 | 289 |
| 271 callbackBoolean(true); | 290 callbackBoolean(true); |
| 272 }); | 291 }); |
| 273 } | 292 } |
| 274 | 293 |
| 275 /** | 294 /** |
| 276 * Shows parsed and merged cards as notifications. | 295 * Shows parsed and combined cards as notifications. |
| 277 * @param {Object.<string, MergedCard>} cards Map from chromeNotificationId to | 296 * @param {Object.<ChromeNotificationId, CombinedCard>} cards Map from |
| 278 * the merged card, containing cards to show. | 297 * chromeNotificationId to the combined card, containing cards to show. |
| 279 * @param {function(CardCreateInfo)=} onCardShown Optional parameter called when | 298 * @param {function(ReceivedNotification)=} onCardShown Optional parameter |
| 280 * each card is shown. | 299 * called when each card is shown. |
| 281 */ | 300 */ |
| 282 function showNotificationCards(cards, onCardShown) { | 301 function showNotificationCards(cards, onCardShown) { |
| 283 console.log('showNotificationCards ' + JSON.stringify(cards)); | 302 console.log('showNotificationCards ' + JSON.stringify(cards)); |
| 284 | 303 |
| 285 instrumented.storage.local.get(['notificationsData', 'recentDismissals'], | 304 instrumented.notifications.getAll(function(notifications) { |
| 286 function(items) { | 305 console.log('showNotificationCards-getAll ' + |
| 287 console.log('showNotificationCards-get ' + | 306 JSON.stringify(notifications)); |
| 288 JSON.stringify(items)); | 307 notifications = notifications || {}; |
| 289 items = items || {}; | |
| 290 items.notificationsData = items.notificationsData || {}; | |
| 291 items.recentDismissals = items.recentDismissals || {}; | |
| 292 | 308 |
| 293 instrumented.notifications.getAll(function(notifications) { | 309 // Mark notifications that didn't receive an update as having received |
| 294 console.log('showNotificationCards-getAll ' + | 310 // an empty update. |
| 295 JSON.stringify(notifications)); | 311 for (var chromeNotificationId in notifications) { |
| 296 notifications = notifications || {}; | 312 cards[chromeNotificationId] = cards[chromeNotificationId] || []; |
| 313 } | |
| 297 | 314 |
| 298 // Build a set of non-expired recent dismissals. It will be used for | 315 /** @type {Object.<string, NotificationDataEntry>} */ |
| 299 // client-side filtering of cards. | 316 var notificationsData = {}; |
| 300 var updatedRecentDismissals = {}; | |
| 301 var currentTimeMs = Date.now(); | |
| 302 for (var chromeNotificationId in items.recentDismissals) { | |
| 303 if (currentTimeMs - items.recentDismissals[chromeNotificationId] < | |
| 304 DISMISS_RETENTION_TIME_MS) { | |
| 305 updatedRecentDismissals[chromeNotificationId] = | |
| 306 items.recentDismissals[chromeNotificationId]; | |
| 307 delete cards[chromeNotificationId]; | |
| 308 } | |
| 309 } | |
| 310 | 317 |
| 311 // Delete notifications that didn't receive an update. | 318 // Create/update/delete notifications. |
| 312 for (var chromeNotificationId in notifications) { | 319 for (var chromeNotificationId in cards) { |
| 313 console.log('showNotificationCards-delete-check ' + | 320 notificationsData[chromeNotificationId] = cardSet.update( |
| 314 chromeNotificationId); | 321 chromeNotificationId, |
| 315 if (!(chromeNotificationId in cards)) { | 322 cards[chromeNotificationId], |
| 316 console.log( | 323 onCardShown); |
| 317 'showNotificationCards-delete ' + chromeNotificationId); | 324 } |
| 318 cardSet.clear(chromeNotificationId, false); | 325 chrome.storage.local.set({notificationsData: notificationsData}); |
| 319 } | 326 }); |
| 320 } | |
| 321 | |
| 322 // Create/update notifications and store their new properties. | |
| 323 var newNotificationsData = {}; | |
| 324 for (var chromeNotificationId in cards) { | |
| 325 var notificationData = | |
| 326 items.notificationsData[chromeNotificationId]; | |
| 327 var previousVersion = notifications[chromeNotificationId] && | |
| 328 notificationData && | |
| 329 notificationData.cardCreateInfo && | |
| 330 notificationData.cardCreateInfo.version; | |
| 331 newNotificationsData[chromeNotificationId] = cardSet.update( | |
| 332 chromeNotificationId, | |
| 333 cards[chromeNotificationId], | |
| 334 previousVersion, | |
| 335 onCardShown); | |
| 336 } | |
| 337 | |
| 338 chrome.storage.local.set({ | |
| 339 notificationsData: newNotificationsData, | |
| 340 recentDismissals: updatedRecentDismissals | |
| 341 }); | |
| 342 }); | |
| 343 }); | |
| 344 } | 327 } |
| 345 | 328 |
| 346 /** | 329 /** |
| 347 * Removes all cards and card state on Google Now close down. | 330 * Removes all cards and card state on Google Now close down. |
| 348 * For example, this occurs when the geolocation preference is unchecked in the | 331 * For example, this occurs when the geolocation preference is unchecked in the |
| 349 * content settings. | 332 * content settings. |
| 350 */ | 333 */ |
| 351 function removeAllCards() { | 334 function removeAllCards() { |
| 352 console.log('removeAllCards'); | 335 console.log('removeAllCards'); |
| 353 | 336 |
| 354 // TODO(robliao): Once Google Now clears its own checkbox in the | 337 // TODO(robliao): Once Google Now clears its own checkbox in the |
| 355 // notifications center and bug 260376 is fixed, the below clearing | 338 // notifications center and bug 260376 is fixed, the below clearing |
| 356 // code is no longer necessary. | 339 // code is no longer necessary. |
| 357 instrumented.notifications.getAll(function(notifications) { | 340 instrumented.notifications.getAll(function(notifications) { |
| 358 notifications = notifications || {}; | 341 notifications = notifications || {}; |
| 359 for (var chromeNotificationId in notifications) { | 342 for (var chromeNotificationId in notifications) { |
| 360 instrumented.notifications.clear(chromeNotificationId, function() {}); | 343 instrumented.notifications.clear(chromeNotificationId, function() {}); |
| 361 } | 344 } |
| 362 chrome.storage.local.remove(['notificationsData', 'notificationGroups']); | 345 chrome.storage.local.remove(['notificationsData', 'notificationGroups']); |
| 363 }); | 346 }); |
| 364 } | 347 } |
| 365 | 348 |
| 366 /** | 349 /** |
| 367 * Merges an unmerged notification into a merged card with same ID. | 350 * Adds a card group into a set of combined cards. |
| 368 * @param {MergedCard=} mergedCard Existing merged card or undefined if a merged | 351 * @param {Object.<ChromeNotificationId, CombinedCard>} combinedCards Map from |
| 369 * card doesn't exist (i.e. we see this ID for the first time while | 352 * chromeNotificationId to a combined card. |
| 370 * merging). | 353 * This is an input/output parameter. |
| 371 * @param {UnmergedNotification} unmergedNotification Notification as it was | 354 * @param {StoredNotificationGroup} storedGroup Group to combine into the |
| 372 * received from the server. | 355 * combined card set. |
| 373 * @param {number} notificationTimestamp The moment the unmerged card was | |
| 374 * received. | |
| 375 * @param {number} notificationGroupRank Rank of the group of the unmerged card. | |
| 376 * @return {MergedCard} Result of merging |unmergedNotification| into | |
| 377 * |mergedCard|. | |
| 378 */ | 356 */ |
| 379 function mergeCards( | 357 function combineGroup(combinedCards, storedGroup) { |
| 380 mergedCard, | 358 for (var i = 0; i < storedGroup.cards.length; i++) { |
| 381 unmergedNotification, | 359 /** @type {ReceivedNotification} */ |
| 382 notificationTimestamp, | 360 var receivedNotification = storedGroup.cards[i]; |
| 383 notificationGroupRank) { | |
| 384 var result = mergedCard || {dismissals: []}; | |
| 385 | 361 |
| 386 var priority = mergedCard ? | 362 /** @type {UncombinedNotification} */ |
| 387 Math.max( | 363 var uncombinedNotification = { |
| 388 mergedCard.notification.priority, | 364 receivedNotification: receivedNotification, |
| 389 unmergedNotification.chromeNotificationOptions.priority) : | 365 showTime: receivedNotification.trigger.showTimeSec && |
| 390 unmergedNotification.chromeNotificationOptions.priority; | 366 (storedGroup.cardsTimestamp + |
| 367 receivedNotification.trigger.showTimeSec * MS_IN_SECOND), | |
| 368 hideTime: storedGroup.cardsTimestamp + | |
| 369 receivedNotification.trigger.hideTimeSec * MS_IN_SECOND | |
| 370 }; | |
| 391 | 371 |
| 392 if (!mergedCard || notificationGroupRank > mergedCard.groupRank) { | 372 var combinedCard = |
| 393 result.groupRank = notificationGroupRank; | 373 combinedCards[receivedNotification.chromeNotificationId] || []; |
| 394 var showTime = unmergedNotification.trigger && | 374 combinedCard.push(uncombinedNotification); |
| 395 unmergedNotification.trigger.showTimeSec && | 375 combinedCards[receivedNotification.chromeNotificationId] = combinedCard; |
| 396 (notificationTimestamp + | |
| 397 unmergedNotification.trigger.showTimeSec * MS_IN_SECOND); | |
| 398 var hideTime = unmergedNotification.trigger && | |
| 399 unmergedNotification.trigger.hideTimeSec && | |
| 400 (notificationTimestamp + | |
| 401 unmergedNotification.trigger.hideTimeSec * MS_IN_SECOND); | |
| 402 result.trigger = { | |
| 403 showTime: showTime, | |
| 404 hideTime: hideTime | |
| 405 }; | |
| 406 } | |
| 407 | |
| 408 if (!mergedCard || notificationTimestamp > mergedCard.timestamp) { | |
| 409 result.timestamp = notificationTimestamp; | |
| 410 result.notification = unmergedNotification.chromeNotificationOptions; | |
| 411 result.actionUrls = unmergedNotification.actionUrls; | |
| 412 result.version = unmergedNotification.version; | |
| 413 } | |
| 414 | |
| 415 result.locationBased = | |
| 416 result.locationBased || unmergedNotification.locationBased; | |
| 417 | |
| 418 result.notification.priority = priority; | |
| 419 var dismissalData = { | |
| 420 notificationId: unmergedNotification.notificationId, | |
| 421 parameters: unmergedNotification.dismissal | |
| 422 }; | |
| 423 result.dismissals.push(dismissalData); | |
| 424 | |
| 425 return result; | |
| 426 } | |
| 427 | |
| 428 /** | |
| 429 * Merges a card group into a set of merged cards. | |
| 430 * @param {Object.<string, MergedCard>} mergedCards Map from | |
| 431 * chromeNotificationId to a merged card. | |
| 432 * This is an input/output parameter. | |
| 433 * @param {StorageGroup} storageGroup Group to merge into the merged card set. | |
| 434 */ | |
| 435 function mergeGroup(mergedCards, storageGroup) { | |
| 436 for (var i = 0; i < storageGroup.cards.length; i++) { | |
| 437 var card = storageGroup.cards[i]; | |
| 438 mergedCards[card.chromeNotificationId] = mergeCards( | |
| 439 mergedCards[card.chromeNotificationId], | |
| 440 card, | |
| 441 storageGroup.cardsTimestamp, | |
| 442 storageGroup.rank); | |
| 443 } | 376 } |
| 444 } | 377 } |
| 445 | 378 |
| 446 /** | 379 /** |
| 447 * Schedules next cards poll. | 380 * Schedules next cards poll. |
| 448 * @param {Object.<string, StorageGroup>} groups Map from group name to group | 381 * @param {Object.<string, StoredNotificationGroup>} groups Map from group name |
| 449 * information. | 382 * to group information. |
| 450 * @param {boolean} isOptedIn True if the user is opted in to Google Now. | 383 * @param {boolean} isOptedIn True if the user is opted in to Google Now. |
| 451 */ | 384 */ |
| 452 function scheduleNextPoll(groups, isOptedIn) { | 385 function scheduleNextPoll(groups, isOptedIn) { |
| 453 if (isOptedIn) { | 386 if (isOptedIn) { |
| 454 var nextPollTime = null; | 387 var nextPollTime = null; |
| 455 | 388 |
| 456 for (var groupName in groups) { | 389 for (var groupName in groups) { |
| 457 var group = groups[groupName]; | 390 var group = groups[groupName]; |
| 458 if (group.nextPollTime !== undefined) { | 391 if (group.nextPollTime !== undefined) { |
| 459 nextPollTime = nextPollTime == null ? | 392 nextPollTime = nextPollTime == null ? |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 473 'GoogleNow', function(params) { | 406 'GoogleNow', function(params) { |
| 474 var optinPollPeriodSeconds = | 407 var optinPollPeriodSeconds = |
| 475 parseInt(params && params.optinPollPeriodSeconds, 10) || | 408 parseInt(params && params.optinPollPeriodSeconds, 10) || |
| 476 DEFAULT_OPTIN_CHECK_PERIOD_SECONDS; | 409 DEFAULT_OPTIN_CHECK_PERIOD_SECONDS; |
| 477 updateCardsAttempts.start(optinPollPeriodSeconds); | 410 updateCardsAttempts.start(optinPollPeriodSeconds); |
| 478 }); | 411 }); |
| 479 } | 412 } |
| 480 } | 413 } |
| 481 | 414 |
| 482 /** | 415 /** |
| 483 * Merges notification groups into a set of Chrome notifications and shows them. | 416 * Combines notification groups into a set of Chrome notifications and shows |
| 484 * @param {Object.<string, StorageGroup>} notificationGroups Map from group name | 417 * them. |
| 485 * to group information. | 418 * @param {Object.<string, StoredNotificationGroup>} notificationGroups Map from |
| 486 * @param {function(CardCreateInfo)=} onCardShown Optional parameter called when | 419 * group name to group information. |
| 487 * each card is shown. | 420 * @param {function(ReceivedNotification)=} onCardShown Optional parameter |
| 421 * called when each card is shown. | |
| 488 */ | 422 */ |
| 489 function mergeAndShowNotificationCards(notificationGroups, onCardShown) { | 423 function combineAndShowNotificationCards(notificationGroups, onCardShown) { |
| 490 var mergedCards = {}; | 424 console.log('combineAndShowNotificationCards ' + |
| 425 JSON.stringify(notificationGroups)); | |
| 426 /** @type {Object.<ChromeNotificationId, CombinedCard>} */ | |
| 427 var combinedCards = {}; | |
| 491 | 428 |
| 492 for (var groupName in notificationGroups) | 429 for (var groupName in notificationGroups) |
| 493 mergeGroup(mergedCards, notificationGroups[groupName]); | 430 combineGroup(combinedCards, notificationGroups[groupName]); |
| 494 | 431 |
| 495 showNotificationCards(mergedCards, onCardShown); | 432 showNotificationCards(combinedCards, onCardShown); |
| 496 } | 433 } |
| 497 | 434 |
| 498 /** | 435 /** |
| 499 * Parses JSON response from the notification server, shows notifications and | 436 * Parses JSON response from the notification server, shows notifications and |
| 500 * schedules next update. | 437 * schedules next update. |
| 501 * @param {string} response Server response. | 438 * @param {string} response Server response. |
| 502 * @param {function(CardCreateInfo)=} onCardShown Optional parameter called when | 439 * @param {function(ReceivedNotification)=} onCardShown Optional parameter |
| 503 * each card is shown. | 440 * called when each card is shown. |
| 504 */ | 441 */ |
| 505 function parseAndShowNotificationCards(response, onCardShown) { | 442 function parseAndShowNotificationCards(response, onCardShown) { |
| 506 console.log('parseAndShowNotificationCards ' + response); | 443 console.log('parseAndShowNotificationCards ' + response); |
| 444 /** @type {ServerResponse} */ | |
| 507 var parsedResponse = JSON.parse(response); | 445 var parsedResponse = JSON.parse(response); |
| 508 | 446 |
| 509 if (parsedResponse.googleNowDisabled) { | 447 if (parsedResponse.googleNowDisabled) { |
| 510 chrome.storage.local.set({googleNowEnabled: false}); | 448 chrome.storage.local.set({googleNowEnabled: false}); |
| 511 // TODO(vadimt): Remove the line below once the server stops sending groups | 449 // TODO(vadimt): Remove the line below once the server stops sending groups |
| 512 // with 'googleNowDisabled' responses. | 450 // with 'googleNowDisabled' responses. |
| 513 parsedResponse.groups = {}; | 451 parsedResponse.groups = {}; |
| 514 // Google Now was enabled; now it's disabled. This is a state change. | 452 // Google Now was enabled; now it's disabled. This is a state change. |
| 515 onStateChange(); | 453 onStateChange(); |
| 516 } | 454 } |
| 517 | 455 |
| 518 var receivedGroups = parsedResponse.groups; | 456 var receivedGroups = parsedResponse.groups; |
| 519 | 457 |
| 520 // Populate groups with corresponding cards. | 458 instrumented.storage.local.get(['notificationGroups', 'recentDismissals'], |
| 521 if (parsedResponse.notifications) { | 459 function(items) { |
| 522 for (var i = 0; i != parsedResponse.notifications.length; ++i) { | |
| 523 var card = parsedResponse.notifications[i]; | |
| 524 var group = receivedGroups[card.groupName]; | |
| 525 group.cards = group.cards || []; | |
| 526 group.cards.push(card); | |
| 527 } | |
| 528 } | |
| 529 | |
| 530 instrumented.storage.local.get('notificationGroups', function(items) { | |
| 531 console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items)); | 460 console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items)); |
|
robliao
2013/12/07 00:51:29
Style: These need to be tabbed over two spaces.
vadimt
2013/12/07 01:04:24
Done.
| |
| 532 items = items || {}; | 461 items = items || {}; |
| 462 /** @type {Object.<string, StoredNotificationGroup>} */ | |
| 533 items.notificationGroups = items.notificationGroups || {}; | 463 items.notificationGroups = items.notificationGroups || {}; |
| 464 /** @type {Object.<NotificationId, number>} */ | |
| 465 items.recentDismissals = items.recentDismissals || {}; | |
| 534 | 466 |
| 467 // Build a set of non-expired recent dismissals. It will be used for | |
| 468 // client-side filtering of cards. | |
| 469 /** @type {Object.<NotificationId, number>} */ | |
| 470 var updatedRecentDismissals = {}; | |
| 535 var now = Date.now(); | 471 var now = Date.now(); |
| 472 for (var notificationId in items.recentDismissals) { | |
| 473 var dismissalAge = now - items.recentDismissals[notificationId]; | |
| 474 if (dismissalAge < DISMISS_RETENTION_TIME_MS) { | |
| 475 updatedRecentDismissals[notificationId] = | |
| 476 items.recentDismissals[notificationId]; | |
| 477 } | |
| 478 } | |
| 479 | |
| 480 // Populate groups with corresponding cards. | |
| 481 if (parsedResponse.notifications) { | |
| 482 for (var i = 0; i < parsedResponse.notifications.length; ++i) { | |
| 483 /** @type {ReceivedNotification} */ | |
| 484 var card = parsedResponse.notifications[i]; | |
| 485 if (!(card.notificationId in updatedRecentDismissals)) { | |
| 486 var group = receivedGroups[card.groupName]; | |
| 487 group.cards = group.cards || []; | |
| 488 group.cards.push(card); | |
| 489 } | |
| 490 } | |
| 491 } | |
| 536 | 492 |
| 537 // Build updated set of groups. | 493 // Build updated set of groups. |
| 538 var updatedGroups = {}; | 494 var updatedGroups = {}; |
| 539 | 495 |
| 540 for (var groupName in receivedGroups) { | 496 for (var groupName in receivedGroups) { |
| 541 var receivedGroup = receivedGroups[groupName]; | 497 var receivedGroup = receivedGroups[groupName]; |
| 542 var storageGroup = items.notificationGroups[groupName] || { | 498 var storedGroup = items.notificationGroups[groupName] || { |
| 543 cards: [], | 499 cards: [], |
| 544 cardsTimestamp: undefined, | 500 cardsTimestamp: undefined, |
| 545 nextPollTime: undefined, | 501 nextPollTime: undefined, |
| 546 rank: undefined | 502 rank: undefined |
| 547 }; | 503 }; |
| 548 | 504 |
| 549 if (receivedGroup.requested) | 505 if (receivedGroup.requested) |
| 550 receivedGroup.cards = receivedGroup.cards || []; | 506 receivedGroup.cards = receivedGroup.cards || []; |
| 551 | 507 |
| 552 if (receivedGroup.cards) { | 508 if (receivedGroup.cards) { |
| 553 // If the group contains a cards update, all its fields will get new | 509 // If the group contains a cards update, all its fields will get new |
| 554 // values. | 510 // values. |
| 555 storageGroup.cards = receivedGroup.cards; | 511 storedGroup.cards = receivedGroup.cards; |
| 556 storageGroup.cardsTimestamp = now; | 512 storedGroup.cardsTimestamp = now; |
| 557 storageGroup.rank = receivedGroup.rank; | 513 storedGroup.rank = receivedGroup.rank; |
| 558 storageGroup.nextPollTime = undefined; | 514 storedGroup.nextPollTime = undefined; |
| 559 // The code below assigns nextPollTime a defined value if | 515 // The code below assigns nextPollTime a defined value if |
| 560 // nextPollSeconds is specified in the received group. | 516 // nextPollSeconds is specified in the received group. |
| 561 // If the group's cards are not updated, and nextPollSeconds is | 517 // If the group's cards are not updated, and nextPollSeconds is |
| 562 // unspecified, this method doesn't change group's nextPollTime. | 518 // unspecified, this method doesn't change group's nextPollTime. |
| 563 } | 519 } |
| 564 | 520 |
| 565 // 'nextPollSeconds' may be sent even for groups that don't contain cards | 521 // 'nextPollSeconds' may be sent even for groups that don't contain cards |
| 566 // updates. | 522 // updates. |
| 567 if (receivedGroup.nextPollSeconds !== undefined) { | 523 if (receivedGroup.nextPollSeconds !== undefined) { |
| 568 storageGroup.nextPollTime = | 524 storedGroup.nextPollTime = |
| 569 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; | 525 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; |
| 570 } | 526 } |
| 571 | 527 |
| 572 updatedGroups[groupName] = storageGroup; | 528 updatedGroups[groupName] = storedGroup; |
| 573 } | 529 } |
| 574 | 530 |
| 575 scheduleNextPoll(updatedGroups, !parsedResponse.googleNowDisabled); | 531 scheduleNextPoll(updatedGroups, !parsedResponse.googleNowDisabled); |
| 576 chrome.storage.local.set({notificationGroups: updatedGroups}); | 532 chrome.storage.local.set({ |
| 577 mergeAndShowNotificationCards(updatedGroups, onCardShown); | 533 notificationGroups: updatedGroups, |
| 534 recentDismissals: updatedRecentDismissals | |
| 535 }); | |
| 536 combineAndShowNotificationCards(updatedGroups, onCardShown); | |
| 578 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); | 537 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); |
| 579 }); | 538 }); |
| 580 } | 539 } |
| 581 | 540 |
| 582 /** | 541 /** |
| 583 * Update Location Cards Shown Count. | 542 * Update Location Cards Shown Count. |
| 584 * @param {Object} cardCreateInfo Card Create Info | 543 * @param {ReceivedNotification} receivedNotification Notification as it was |
| 544 * received from the server. | |
| 585 */ | 545 */ |
| 586 function countLocationCard(cardCreateInfo) { | 546 function countLocationCard(receivedNotification) { |
| 587 if (cardCreateInfo.locationBased) { | 547 if (receivedNotification.locationBased) { |
| 588 localStorage['locationCardsShown']++; | 548 localStorage['locationCardsShown']++; |
| 589 } | 549 } |
| 590 } | 550 } |
| 591 | 551 |
| 592 /** | 552 /** |
| 593 * Requests notification cards from the server for specified groups. | 553 * Requests notification cards from the server for specified groups. |
| 594 * @param {Array.<string>} groupNames Names of groups that need to be refreshed. | 554 * @param {Array.<string>} groupNames Names of groups that need to be refreshed. |
| 595 */ | 555 */ |
| 596 function requestNotificationGroups(groupNames) { | 556 function requestNotificationGroups(groupNames) { |
| 597 console.log('requestNotificationGroups from ' + NOTIFICATION_CARDS_URL + | 557 console.log('requestNotificationGroups from ' + NOTIFICATION_CARDS_URL + |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 613 }); | 573 }); |
| 614 | 574 |
| 615 console.log('requestNotificationGroups: request=' + requestParameters); | 575 console.log('requestNotificationGroups: request=' + requestParameters); |
| 616 | 576 |
| 617 var request = buildServerRequest('GET', 'notifications' + requestParameters); | 577 var request = buildServerRequest('GET', 'notifications' + requestParameters); |
| 618 | 578 |
| 619 request.onloadend = function(event) { | 579 request.onloadend = function(event) { |
| 620 console.log('requestNotificationGroups-onloadend ' + request.status); | 580 console.log('requestNotificationGroups-onloadend ' + request.status); |
| 621 if (request.status == HTTP_OK) { | 581 if (request.status == HTTP_OK) { |
| 622 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); | 582 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); |
| 623 parseAndShowNotificationCards(request.response, cardShownCallback); | 583 parseAndShowNotificationCards(request.responseText, cardShownCallback); |
| 624 } | 584 } |
| 625 }; | 585 }; |
| 626 | 586 |
| 627 setAuthorization(request, function(success) { | 587 setAuthorization(request, function(success) { |
| 628 if (success) | 588 if (success) |
| 629 request.send(); | 589 request.send(); |
| 630 }); | 590 }); |
| 631 } | 591 } |
| 632 | 592 |
| 633 /** | 593 /** |
| 634 * Requests the account opted-in state from the server. | 594 * Requests the account opted-in state from the server. |
| 635 * @param {function()} optedInCallback Function that will be called if | 595 * @param {function()} optedInCallback Function that will be called if |
| 636 * opted-in state is 'true'. | 596 * opted-in state is 'true'. |
| 637 */ | 597 */ |
| 638 function requestOptedIn(optedInCallback) { | 598 function requestOptedIn(optedInCallback) { |
| 639 console.log('requestOptedIn from ' + NOTIFICATION_CARDS_URL); | 599 console.log('requestOptedIn from ' + NOTIFICATION_CARDS_URL); |
| 640 | 600 |
| 641 var request = buildServerRequest('GET', 'settings/optin'); | 601 var request = buildServerRequest('GET', 'settings/optin'); |
| 642 | 602 |
| 643 request.onloadend = function(event) { | 603 request.onloadend = function(event) { |
| 644 console.log( | 604 console.log( |
| 645 'requestOptedIn-onloadend ' + request.status + ' ' + request.response); | 605 'requestOptedIn-onloadend ' + request.status + ' ' + request.response); |
| 646 if (request.status == HTTP_OK) { | 606 if (request.status == HTTP_OK) { |
| 647 var parsedResponse = JSON.parse(request.response); | 607 var parsedResponse = JSON.parse(request.responseText); |
| 648 if (parsedResponse.value) { | 608 if (parsedResponse.value) { |
| 649 chrome.storage.local.set({googleNowEnabled: true}); | 609 chrome.storage.local.set({googleNowEnabled: true}); |
| 650 optedInCallback(); | 610 optedInCallback(); |
| 651 // Google Now was disabled, now it's enabled. This is a state change. | 611 // Google Now was disabled, now it's enabled. This is a state change. |
| 652 onStateChange(); | 612 onStateChange(); |
| 653 } else { | 613 } else { |
| 654 scheduleNextPoll({}, false); | 614 scheduleNextPoll({}, false); |
| 655 } | 615 } |
| 656 } | 616 } |
| 657 }; | 617 }; |
| 658 | 618 |
| 659 setAuthorization(request, function(success) { | 619 setAuthorization(request, function(success) { |
| 660 if (success) | 620 if (success) |
| 661 request.send(); | 621 request.send(); |
| 662 }); | 622 }); |
| 663 } | 623 } |
| 664 | 624 |
| 665 /** | 625 /** |
| 666 * Requests notification cards from the server. | 626 * Requests notification cards from the server. |
| 667 * @param {Location} position Location of this computer. | 627 * @param {Location=} position Location of this computer. |
| 668 */ | 628 */ |
| 669 function requestNotificationCards(position) { | 629 function requestNotificationCards(position) { |
| 670 console.log('requestNotificationCards ' + JSON.stringify(position)); | 630 console.log('requestNotificationCards ' + JSON.stringify(position)); |
| 671 | 631 |
| 672 instrumented.storage.local.get( | 632 instrumented.storage.local.get( |
| 673 ['notificationGroups', 'googleNowEnabled'], function(items) { | 633 ['notificationGroups', 'googleNowEnabled'], function(items) { |
| 674 console.log('requestNotificationCards-storage-get ' + | 634 console.log('requestNotificationCards-storage-get ' + |
| 675 JSON.stringify(items)); | 635 JSON.stringify(items)); |
| 676 items = items || {}; | 636 items = items || {}; |
| 637 /** @type {Object.<string, StoredNotificationGroup>} */ | |
| 638 items.notificationGroups = items.notificationGroups || {}; | |
| 677 | 639 |
| 678 var groupsToRequest = []; | 640 var groupsToRequest = []; |
| 679 | 641 |
| 680 if (items.notificationGroups) { | 642 var now = Date.now(); |
| 681 var now = Date.now(); | |
| 682 | 643 |
| 683 for (var groupName in items.notificationGroups) { | 644 for (var groupName in items.notificationGroups) { |
| 684 var group = items.notificationGroups[groupName]; | 645 var group = items.notificationGroups[groupName]; |
| 685 if (group.nextPollTime !== undefined && group.nextPollTime <= now) | 646 if (group.nextPollTime !== undefined && group.nextPollTime <= now) |
| 686 groupsToRequest.push(groupName); | 647 groupsToRequest.push(groupName); |
| 687 } | |
| 688 } | 648 } |
| 689 | 649 |
| 690 if (items.googleNowEnabled) { | 650 if (items.googleNowEnabled) { |
| 691 requestNotificationGroups(groupsToRequest); | 651 requestNotificationGroups(groupsToRequest); |
| 692 } else { | 652 } else { |
| 693 requestOptedIn(function() { | 653 requestOptedIn(function() { |
| 694 requestNotificationGroups(groupsToRequest); | 654 requestNotificationGroups(groupsToRequest); |
| 695 }); | 655 }); |
| 696 } | 656 } |
| 697 }); | 657 }); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 729 * Stops getting the location. | 689 * Stops getting the location. |
| 730 */ | 690 */ |
| 731 function stopRequestLocation() { | 691 function stopRequestLocation() { |
| 732 console.log('stopRequestLocation'); | 692 console.log('stopRequestLocation'); |
| 733 chrome.location.clearWatch(LOCATION_WATCH_NAME); | 693 chrome.location.clearWatch(LOCATION_WATCH_NAME); |
| 734 } | 694 } |
| 735 | 695 |
| 736 /** | 696 /** |
| 737 * Obtains new location; requests and shows notification cards based on this | 697 * Obtains new location; requests and shows notification cards based on this |
| 738 * location. | 698 * location. |
| 739 * @param {Location} position Location of this computer. | 699 * @param {Location=} position Location of this computer. |
| 740 */ | 700 */ |
| 741 function updateNotificationsCards(position) { | 701 function updateNotificationsCards(position) { |
| 742 console.log('updateNotificationsCards ' + JSON.stringify(position) + | 702 console.log('updateNotificationsCards ' + JSON.stringify(position) + |
| 743 ' @' + new Date()); | 703 ' @' + new Date()); |
| 744 tasks.add(UPDATE_CARDS_TASK_NAME, function() { | 704 tasks.add(UPDATE_CARDS_TASK_NAME, function() { |
| 745 console.log('updateNotificationsCards-task-begin'); | 705 console.log('updateNotificationsCards-task-begin'); |
| 746 updateCardsAttempts.isRunning(function(running) { | 706 updateCardsAttempts.isRunning(function(running) { |
| 747 if (running) { | 707 if (running) { |
| 748 updateCardsAttempts.planForNext(function() { | 708 updateCardsAttempts.planForNext(function() { |
| 749 processPendingDismissals(function(success) { | 709 processPendingDismissals(function(success) { |
| 750 if (success) { | 710 if (success) { |
| 751 // The cards are requested only if there are no unsent dismissals. | 711 // The cards are requested only if there are no unsent dismissals. |
| 752 requestNotificationCards(position); | 712 requestNotificationCards(position); |
| 753 } | 713 } |
| 754 }); | 714 }); |
| 755 }); | 715 }); |
| 756 } | 716 } |
| 757 }); | 717 }); |
| 758 }); | 718 }); |
| 759 } | 719 } |
| 760 | 720 |
| 761 /** | 721 /** |
| 762 * Sends a server request to dismiss a card. | 722 * Sends a server request to dismiss a card. |
| 763 * @param {string} chromeNotificationId chrome.notifications ID of the card. | 723 * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of |
| 724 * the card. | |
| 764 * @param {number} dismissalTimeMs Time of the user's dismissal of the card in | 725 * @param {number} dismissalTimeMs Time of the user's dismissal of the card in |
| 765 * milliseconds since epoch. | 726 * milliseconds since epoch. |
| 766 * @param {DismissalData} dismissalData Data to build a dismissal request. | 727 * @param {DismissalData} dismissalData Data to build a dismissal request. |
| 767 * @param {function(boolean)} callbackBoolean Completion callback with 'done' | 728 * @param {function(boolean)} callbackBoolean Completion callback with 'done' |
| 768 * parameter. | 729 * parameter. |
| 769 */ | 730 */ |
| 770 function requestCardDismissal( | 731 function requestCardDismissal( |
| 771 chromeNotificationId, dismissalTimeMs, dismissalData, callbackBoolean) { | 732 chromeNotificationId, dismissalTimeMs, dismissalData, callbackBoolean) { |
| 772 console.log('requestDismissingCard ' + chromeNotificationId + ' from ' + | 733 console.log('requestDismissingCard ' + chromeNotificationId + |
| 773 NOTIFICATION_CARDS_URL); | 734 ' from ' + NOTIFICATION_CARDS_URL + |
| 735 ', dismissalData=' + JSON.stringify(dismissalData)); | |
| 774 | 736 |
| 775 var dismissalAge = Date.now() - dismissalTimeMs; | 737 var dismissalAge = Date.now() - dismissalTimeMs; |
| 776 | 738 |
| 777 if (dismissalAge > MAXIMUM_DISMISSAL_AGE_MS) { | 739 if (dismissalAge > MAXIMUM_DISMISSAL_AGE_MS) { |
| 778 callbackBoolean(true); | 740 callbackBoolean(true); |
| 779 return; | 741 return; |
| 780 } | 742 } |
| 781 | 743 |
| 782 recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL); | 744 recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL); |
| 783 | 745 |
| 784 var request = 'notifications/' + dismissalData.notificationId + | 746 var requestParameters = 'notifications/' + dismissalData.notificationId + |
| 785 '?age=' + dismissalAge + | 747 '?age=' + dismissalAge + |
| 786 '&chromeNotificationId=' + chromeNotificationId; | 748 '&chromeNotificationId=' + chromeNotificationId; |
| 787 | 749 |
| 788 for (var paramField in dismissalData.parameters) | 750 for (var paramField in dismissalData.parameters) |
| 789 request += ('&' + paramField + '=' + dismissalData.parameters[paramField]); | 751 requestParameters += ('&' + paramField + |
| 752 '=' + dismissalData.parameters[paramField]); | |
| 790 | 753 |
| 791 console.log('requestCardDismissal: request=' + request); | 754 console.log('requestCardDismissal: requestParameters=' + requestParameters); |
| 792 | 755 |
| 793 var request = buildServerRequest('DELETE', request); | 756 var request = buildServerRequest('DELETE', requestParameters); |
| 794 request.onloadend = function(event) { | 757 request.onloadend = function(event) { |
| 795 console.log('requestDismissingCard-onloadend ' + request.status); | 758 console.log('requestDismissingCard-onloadend ' + request.status); |
| 796 if (request.status == HTTP_NOCONTENT) | 759 if (request.status == HTTP_NOCONTENT) |
| 797 recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS); | 760 recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS); |
| 798 | 761 |
| 799 // A dismissal doesn't require further retries if it was successful or | 762 // A dismissal doesn't require further retries if it was successful or |
| 800 // doesn't have a chance for successful completion. | 763 // doesn't have a chance for successful completion. |
| 801 var done = request.status == HTTP_NOCONTENT || | 764 var done = request.status == HTTP_NOCONTENT || |
| 802 request.status == HTTP_BAD_REQUEST || | 765 request.status == HTTP_BAD_REQUEST || |
| 803 request.status == HTTP_METHOD_NOT_ALLOWED; | 766 request.status == HTTP_METHOD_NOT_ALLOWED; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 816 * Tries to send dismiss requests for all pending dismissals. | 779 * Tries to send dismiss requests for all pending dismissals. |
| 817 * @param {function(boolean)} callbackBoolean Completion callback with 'success' | 780 * @param {function(boolean)} callbackBoolean Completion callback with 'success' |
| 818 * parameter. Success means that no pending dismissals are left. | 781 * parameter. Success means that no pending dismissals are left. |
| 819 */ | 782 */ |
| 820 function processPendingDismissals(callbackBoolean) { | 783 function processPendingDismissals(callbackBoolean) { |
| 821 instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'], | 784 instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'], |
| 822 function(items) { | 785 function(items) { |
| 823 console.log('processPendingDismissals-storage-get ' + | 786 console.log('processPendingDismissals-storage-get ' + |
| 824 JSON.stringify(items)); | 787 JSON.stringify(items)); |
| 825 items = items || {}; | 788 items = items || {}; |
| 789 /** @type {Array.<PendingDismissal>} */ | |
| 826 items.pendingDismissals = items.pendingDismissals || []; | 790 items.pendingDismissals = items.pendingDismissals || []; |
| 791 /** @type {Object.<NotificationId, number>} */ | |
| 827 items.recentDismissals = items.recentDismissals || {}; | 792 items.recentDismissals = items.recentDismissals || {}; |
| 828 | 793 |
| 829 var dismissalsChanged = false; | 794 var dismissalsChanged = false; |
| 830 | 795 |
| 831 function onFinish(success) { | 796 function onFinish(success) { |
| 832 if (dismissalsChanged) { | 797 if (dismissalsChanged) { |
| 833 chrome.storage.local.set({ | 798 chrome.storage.local.set({ |
| 834 pendingDismissals: items.pendingDismissals, | 799 pendingDismissals: items.pendingDismissals, |
| 835 recentDismissals: items.recentDismissals | 800 recentDismissals: items.recentDismissals |
| 836 }); | 801 }); |
| 837 } | 802 } |
| 838 callbackBoolean(success); | 803 callbackBoolean(success); |
| 839 } | 804 } |
| 840 | 805 |
| 841 function doProcessDismissals() { | 806 function doProcessDismissals() { |
| 842 if (items.pendingDismissals.length == 0) { | 807 if (items.pendingDismissals.length == 0) { |
| 843 dismissalAttempts.stop(); | 808 dismissalAttempts.stop(); |
| 844 onFinish(true); | 809 onFinish(true); |
| 845 return; | 810 return; |
| 846 } | 811 } |
| 847 | 812 |
| 848 // Send dismissal for the first card, and if successful, repeat | 813 // Send dismissal for the first card, and if successful, repeat |
| 849 // recursively with the rest. | 814 // recursively with the rest. |
| 815 /** @type {PendingDismissal} */ | |
| 850 var dismissal = items.pendingDismissals[0]; | 816 var dismissal = items.pendingDismissals[0]; |
| 851 requestCardDismissal( | 817 requestCardDismissal( |
| 852 dismissal.chromeNotificationId, | 818 dismissal.chromeNotificationId, |
| 853 dismissal.time, | 819 dismissal.time, |
| 854 dismissal.dismissalData, | 820 dismissal.dismissalData, |
| 855 function(done) { | 821 function(done) { |
| 856 if (done) { | 822 if (done) { |
| 857 dismissalsChanged = true; | 823 dismissalsChanged = true; |
| 858 items.pendingDismissals.splice(0, 1); | 824 items.pendingDismissals.splice(0, 1); |
| 859 items.recentDismissals[dismissal.chromeNotificationId] = | 825 items.recentDismissals[ |
| 826 dismissal.dismissalData.notificationId] = | |
| 860 Date.now(); | 827 Date.now(); |
| 861 doProcessDismissals(); | 828 doProcessDismissals(); |
| 862 } else { | 829 } else { |
| 863 onFinish(false); | 830 onFinish(false); |
| 864 } | 831 } |
| 865 }); | 832 }); |
| 866 } | 833 } |
| 867 | 834 |
| 868 doProcessDismissals(); | 835 doProcessDismissals(); |
| 869 }); | 836 }); |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 888 instrumented.tabs.create({url: url}, function(tab) { | 855 instrumented.tabs.create({url: url}, function(tab) { |
| 889 if (tab) | 856 if (tab) |
| 890 chrome.windows.update(tab.windowId, {focused: true}); | 857 chrome.windows.update(tab.windowId, {focused: true}); |
| 891 else | 858 else |
| 892 chrome.windows.create({url: url, focused: true}); | 859 chrome.windows.create({url: url, focused: true}); |
| 893 }); | 860 }); |
| 894 } | 861 } |
| 895 | 862 |
| 896 /** | 863 /** |
| 897 * Opens URL corresponding to the clicked part of the notification. | 864 * Opens URL corresponding to the clicked part of the notification. |
| 898 * @param {string} chromeNotificationId chrome.notifications ID of the card. | 865 * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of |
| 899 * @param {function(Object): string} selector Function that extracts the url for | 866 * the card. |
| 900 * the clicked area from the button action URLs info. | 867 * @param {function((ActionUrls|undefined)): (string|undefined)} selector |
| 868 * Function that extracts the url for the clicked area from the button | |
| 869 * action URLs info. | |
| 901 */ | 870 */ |
| 902 function onNotificationClicked(chromeNotificationId, selector) { | 871 function onNotificationClicked(chromeNotificationId, selector) { |
| 903 instrumented.storage.local.get('notificationsData', function(items) { | 872 instrumented.storage.local.get('notificationsData', function(items) { |
| 873 /** @type {(NotificationDataEntry|undefined)} */ | |
| 904 var notificationData = items && | 874 var notificationData = items && |
| 905 items.notificationsData && | 875 items.notificationsData && |
| 906 items.notificationsData[chromeNotificationId]; | 876 items.notificationsData[chromeNotificationId]; |
| 907 | 877 |
| 908 if (!notificationData) | 878 if (!notificationData) |
| 909 return; | 879 return; |
| 910 | 880 |
| 911 var url = selector(notificationData.actionUrls); | 881 var url = selector(notificationData.actionUrls); |
| 912 if (!url) | 882 if (!url) |
| 913 return; | 883 return; |
| 914 | 884 |
| 915 openUrl(url); | 885 openUrl(url); |
| 916 }); | 886 }); |
| 917 } | 887 } |
| 918 | 888 |
| 919 /** | 889 /** |
| 920 * Callback for chrome.notifications.onClosed event. | 890 * Callback for chrome.notifications.onClosed event. |
| 921 * @param {string} chromeNotificationId chrome.notifications ID of the card. | 891 * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of |
| 892 * the card. | |
| 922 * @param {boolean} byUser Whether the notification was closed by the user. | 893 * @param {boolean} byUser Whether the notification was closed by the user. |
| 923 */ | 894 */ |
| 924 function onNotificationClosed(chromeNotificationId, byUser) { | 895 function onNotificationClosed(chromeNotificationId, byUser) { |
| 925 if (!byUser) | 896 if (!byUser) |
| 926 return; | 897 return; |
| 927 | 898 |
| 928 // At this point we are guaranteed that the notification is a now card. | 899 // At this point we are guaranteed that the notification is a now card. |
| 929 chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed'); | 900 chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed'); |
| 930 | 901 |
| 931 tasks.add(DISMISS_CARD_TASK_NAME, function() { | 902 tasks.add(DISMISS_CARD_TASK_NAME, function() { |
| 932 dismissalAttempts.start(); | 903 dismissalAttempts.start(); |
| 933 | 904 |
| 934 instrumented.storage.local.get( | 905 instrumented.storage.local.get( |
| 935 ['pendingDismissals', 'notificationsData'], function(items) { | 906 ['pendingDismissals', 'notificationsData'], function(items) { |
| 936 items = items || {}; | 907 items = items || {}; |
| 908 /** @type {Array.<PendingDismissal>} */ | |
| 937 items.pendingDismissals = items.pendingDismissals || []; | 909 items.pendingDismissals = items.pendingDismissals || []; |
| 910 /** @type {Object.<string, NotificationDataEntry>} */ | |
| 938 items.notificationsData = items.notificationsData || {}; | 911 items.notificationsData = items.notificationsData || {}; |
| 939 | 912 |
| 940 // Deleting the notification in case it was re-added while this task was | 913 /** @type {NotificationDataEntry} */ |
| 941 // scheduled, waiting for execution; also cleaning notification's data | 914 var notificationData = items.notificationsData[chromeNotificationId] || { |
| 942 // from storage. | 915 timestamp: Date.now(), |
| 943 cardSet.clear(chromeNotificationId, true); | 916 combinedCard: [] |
| 917 }; | |
| 944 | 918 |
| 945 var notificationData = items.notificationsData[chromeNotificationId]; | 919 var dismissalResult = |
| 920 cardSet.onDismissal(chromeNotificationId, notificationData); | |
| 946 | 921 |
| 947 if (notificationData && notificationData.dismissals) { | 922 for (var i = 0; i < dismissalResult.dismissals.length; i++) { |
| 948 for (var i = 0; i < notificationData.dismissals.length; i++) { | 923 /** @type {PendingDismissal} */ |
| 949 var dismissal = { | 924 var dismissal = { |
| 950 chromeNotificationId: chromeNotificationId, | 925 chromeNotificationId: chromeNotificationId, |
| 951 time: Date.now(), | 926 time: Date.now(), |
| 952 dismissalData: notificationData.dismissals[i] | 927 dismissalData: dismissalResult.dismissals[i] |
| 953 }; | 928 }; |
| 954 items.pendingDismissals.push(dismissal); | 929 items.pendingDismissals.push(dismissal); |
| 955 } | 930 } |
| 956 | 931 |
| 957 chrome.storage.local.set({pendingDismissals: items.pendingDismissals}); | 932 items.notificationsData[chromeNotificationId] = |
| 958 } | 933 dismissalResult.notificationData; |
| 934 | |
| 935 chrome.storage.local.set({ | |
| 936 pendingDismissals: items.pendingDismissals, | |
| 937 notificationsData: items.notificationsData | |
| 938 }); | |
| 959 | 939 |
| 960 processPendingDismissals(function(success) {}); | 940 processPendingDismissals(function(success) {}); |
| 961 }); | 941 }); |
| 962 }); | 942 }); |
| 963 } | 943 } |
| 964 | 944 |
| 965 /** | 945 /** |
| 966 * Initializes the polling system to start monitoring location and fetching | 946 * Initializes the polling system to start monitoring location and fetching |
| 967 * cards. | 947 * cards. |
| 968 */ | 948 */ |
| (...skipping 164 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1133 instrumented.runtime.onStartup.addListener(function() { | 1113 instrumented.runtime.onStartup.addListener(function() { |
| 1134 console.log('onStartup'); | 1114 console.log('onStartup'); |
| 1135 | 1115 |
| 1136 // Show notifications received by earlier polls. Doing this as early as | 1116 // Show notifications received by earlier polls. Doing this as early as |
| 1137 // possible to reduce latency of showing first notifications. This mimics how | 1117 // possible to reduce latency of showing first notifications. This mimics how |
| 1138 // persistent notifications will work. | 1118 // persistent notifications will work. |
| 1139 tasks.add(SHOW_ON_START_TASK_NAME, function() { | 1119 tasks.add(SHOW_ON_START_TASK_NAME, function() { |
| 1140 instrumented.storage.local.get('notificationGroups', function(items) { | 1120 instrumented.storage.local.get('notificationGroups', function(items) { |
| 1141 console.log('onStartup-get ' + JSON.stringify(items)); | 1121 console.log('onStartup-get ' + JSON.stringify(items)); |
| 1142 items = items || {}; | 1122 items = items || {}; |
| 1123 /** @type {Object.<string, StoredNotificationGroup>} */ | |
| 1143 items.notificationGroups = items.notificationGroups || {}; | 1124 items.notificationGroups = items.notificationGroups || {}; |
| 1144 | 1125 |
| 1145 mergeAndShowNotificationCards(items.notificationGroups); | 1126 combineAndShowNotificationCards(items.notificationGroups); |
| 1146 }); | 1127 }); |
| 1147 }); | 1128 }); |
| 1148 | 1129 |
| 1149 initialize(); | 1130 initialize(); |
| 1150 }); | 1131 }); |
| 1151 | 1132 |
| 1152 instrumented. | 1133 instrumented. |
| 1153 preferencesPrivate. | 1134 preferencesPrivate. |
| 1154 googleGeolocationAccessEnabled. | 1135 googleGeolocationAccessEnabled. |
| 1155 onChange. | 1136 onChange. |
| (...skipping 15 matching lines...) Expand all Loading... | |
| 1171 return actionUrls && actionUrls.messageUrl; | 1152 return actionUrls && actionUrls.messageUrl; |
| 1172 }); | 1153 }); |
| 1173 }); | 1154 }); |
| 1174 | 1155 |
| 1175 instrumented.notifications.onButtonClicked.addListener( | 1156 instrumented.notifications.onButtonClicked.addListener( |
| 1176 function(chromeNotificationId, buttonIndex) { | 1157 function(chromeNotificationId, buttonIndex) { |
| 1177 chrome.metricsPrivate.recordUserAction( | 1158 chrome.metricsPrivate.recordUserAction( |
| 1178 'GoogleNow.ButtonClicked' + buttonIndex); | 1159 'GoogleNow.ButtonClicked' + buttonIndex); |
| 1179 onNotificationClicked(chromeNotificationId, function(actionUrls) { | 1160 onNotificationClicked(chromeNotificationId, function(actionUrls) { |
| 1180 var url = actionUrls.buttonUrls[buttonIndex]; | 1161 var url = actionUrls.buttonUrls[buttonIndex]; |
| 1181 verify(url, 'onButtonClicked: no url for a button'); | 1162 verify(url !== undefined, 'onButtonClicked: no url for a button'); |
| 1182 return url; | 1163 return url; |
| 1183 }); | 1164 }); |
| 1184 }); | 1165 }); |
| 1185 | 1166 |
| 1186 instrumented.notifications.onClosed.addListener(onNotificationClosed); | 1167 instrumented.notifications.onClosed.addListener(onNotificationClosed); |
| 1187 | 1168 |
| 1188 instrumented.notifications.onPermissionLevelChanged.addListener( | 1169 instrumented.notifications.onPermissionLevelChanged.addListener( |
| 1189 function(permissionLevel) { | 1170 function(permissionLevel) { |
| 1190 console.log('Notifications permissionLevel Change'); | 1171 console.log('Notifications permissionLevel Change'); |
| 1191 onStateChange(); | 1172 onStateChange(); |
| (...skipping 23 matching lines...) Expand all Loading... | |
| 1215 if (!items) | 1196 if (!items) |
| 1216 return; | 1197 return; |
| 1217 | 1198 |
| 1218 // If this is the first time we get lastPollNowPayloads, initialize it. | 1199 // If this is the first time we get lastPollNowPayloads, initialize it. |
| 1219 items.lastPollNowPayloads = items.lastPollNowPayloads || {}; | 1200 items.lastPollNowPayloads = items.lastPollNowPayloads || {}; |
| 1220 | 1201 |
| 1221 if (items.lastPollNowPayloads[message.subchannelId] != | 1202 if (items.lastPollNowPayloads[message.subchannelId] != |
| 1222 message.payload) { | 1203 message.payload) { |
| 1223 items.lastPollNowPayloads[message.subchannelId] = message.payload; | 1204 items.lastPollNowPayloads[message.subchannelId] = message.payload; |
| 1224 | 1205 |
| 1206 /** @type {Object.<string, StoredNotificationGroup>} */ | |
| 1225 items.notificationGroups = items.notificationGroups || {}; | 1207 items.notificationGroups = items.notificationGroups || {}; |
| 1226 items.notificationGroups['PUSH' + message.subchannelId] = { | 1208 items.notificationGroups['PUSH' + message.subchannelId] = { |
| 1227 cards: [], | 1209 cards: [], |
| 1228 nextPollTime: Date.now() | 1210 nextPollTime: Date.now() |
| 1229 }; | 1211 }; |
| 1230 | 1212 |
| 1231 chrome.storage.local.set({ | 1213 chrome.storage.local.set({ |
| 1232 lastPollNowPayloads: items.lastPollNowPayloads, | 1214 lastPollNowPayloads: items.lastPollNowPayloads, |
| 1233 notificationGroups: items.notificationGroups | 1215 notificationGroups: items.notificationGroups |
| 1234 }); | 1216 }); |
| 1235 | 1217 |
| 1236 updateNotificationsCards(); | 1218 updateNotificationsCards(); |
| 1237 } | 1219 } |
| 1238 }); | 1220 }); |
| 1239 }); | 1221 }); |
| 1240 } | 1222 } |
| 1241 }); | 1223 }); |
| OLD | NEW |