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 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 67 * Time we keep retrying dismissals. | 67 * Time we keep retrying dismissals. |
| 68 */ | 68 */ |
| 69 var MAXIMUM_DISMISSAL_AGE_MS = 24 * 60 * 60 * 1000; // 1 day | 69 var MAXIMUM_DISMISSAL_AGE_MS = 24 * 60 * 60 * 1000; // 1 day |
| 70 | 70 |
| 71 /** | 71 /** |
| 72 * Time we keep dismissals after successful server dismiss requests. | 72 * Time we keep dismissals after successful server dismiss requests. |
| 73 */ | 73 */ |
| 74 var DISMISS_RETENTION_TIME_MS = 20 * 60 * 1000; // 20 minutes | 74 var DISMISS_RETENTION_TIME_MS = 20 * 60 * 1000; // 20 minutes |
| 75 | 75 |
| 76 /** | 76 /** |
| 77 * Default period for checking whether the user is opted in to Google Now. | |
| 78 */ | |
| 79 var DEFAULT_OPTIN_CHECK_PERIOD_SECONDS = 60 * 60 * 24 * 7; // 1 week | |
| 80 | |
| 81 /** | |
| 77 * Names for tasks that can be created by the extension. | 82 * Names for tasks that can be created by the extension. |
| 78 */ | 83 */ |
| 79 var UPDATE_CARDS_TASK_NAME = 'update-cards'; | 84 var UPDATE_CARDS_TASK_NAME = 'update-cards'; |
| 80 var DISMISS_CARD_TASK_NAME = 'dismiss-card'; | 85 var DISMISS_CARD_TASK_NAME = 'dismiss-card'; |
| 81 var RETRY_DISMISS_TASK_NAME = 'retry-dismiss'; | 86 var RETRY_DISMISS_TASK_NAME = 'retry-dismiss'; |
| 82 var STATE_CHANGED_TASK_NAME = 'state-changed'; | 87 var STATE_CHANGED_TASK_NAME = 'state-changed'; |
| 83 var SHOW_ON_START_TASK_NAME = 'show-cards-on-start'; | 88 var SHOW_ON_START_TASK_NAME = 'show-cards-on-start'; |
| 84 var ON_PUSH_MESSAGE_START_TASK_NAME = 'on-push-message'; | 89 var ON_PUSH_MESSAGE_START_TASK_NAME = 'on-push-message'; |
| 85 | 90 |
| 86 var LOCATION_WATCH_NAME = 'location-watch'; | 91 var LOCATION_WATCH_NAME = 'location-watch'; |
| (...skipping 328 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 415 mergedCards[card.chromeNotificationId], | 420 mergedCards[card.chromeNotificationId], |
| 416 card, | 421 card, |
| 417 storageGroup.cardsTimestamp, | 422 storageGroup.cardsTimestamp, |
| 418 storageGroup.rank); | 423 storageGroup.rank); |
| 419 } | 424 } |
| 420 } | 425 } |
| 421 | 426 |
| 422 /** | 427 /** |
| 423 * Schedules next cards poll. | 428 * Schedules next cards poll. |
| 424 * @param {Object.<string, StorageGroup>} groups Map from group name to group | 429 * @param {Object.<string, StorageGroup>} groups Map from group name to group |
| 425 * information. | 430 * information. If this map is empty, the user is not opted in to Google |
|
skare_
2013/10/31 00:49:21
consider making signed-in state an explicit param
vadimt
2013/10/31 14:36:00
I thought it would make sense as is to keep the ex
rgustafson
2013/10/31 18:37:59
It's important to handle the possibility of no gro
vadimt
2013/10/31 19:59:57
Added parameter. Restored old behavior for opted-i
| |
| 431 * Now. | |
| 426 */ | 432 */ |
| 427 function scheduleNextPoll(groups) { | 433 function scheduleNextPoll(groups) { |
| 428 var nextPollTime = null; | 434 var nextPollTime = null; |
| 429 | 435 |
| 430 for (var groupName in groups) { | 436 for (var groupName in groups) { |
| 431 var group = groups[groupName]; | 437 var group = groups[groupName]; |
| 432 if (group.nextPollTime !== undefined) { | 438 if (group.nextPollTime !== undefined) { |
| 433 nextPollTime = nextPollTime == null ? | 439 nextPollTime = nextPollTime == null ? |
| 434 group.nextPollTime : Math.min(group.nextPollTime, nextPollTime); | 440 group.nextPollTime : Math.min(group.nextPollTime, nextPollTime); |
| 435 } | 441 } |
| 436 } | 442 } |
| 437 | 443 |
| 438 // At least one of the groups must have nextPollTime. | 444 if (nextPollTime != null) { |
| 439 verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null'); | 445 var nextPollDelaySeconds = Math.max( |
| 440 | 446 (nextPollTime - Date.now()) / MS_IN_SECOND, |
| 441 var nextPollDelaySeconds = Math.max( | 447 MINIMUM_POLLING_PERIOD_SECONDS); |
| 442 (nextPollTime - Date.now()) / MS_IN_SECOND, | 448 updateCardsAttempts.start(nextPollDelaySeconds); |
| 443 MINIMUM_POLLING_PERIOD_SECONDS); | 449 } else { |
| 444 updateCardsAttempts.start(nextPollDelaySeconds); | 450 instrumented.metricsPrivate.getVariationParams( |
| 451 'GoogleNow', function(params) { | |
| 452 var optinPollPeriodSeconds = | |
| 453 parseInt(params && params.optinPollPeriodSeconds, 10) || | |
| 454 DEFAULT_OPTIN_CHECK_PERIOD_SECONDS; | |
| 455 updateCardsAttempts.start(optinPollPeriodSeconds); | |
| 456 }); | |
| 457 } | |
| 445 } | 458 } |
| 446 | 459 |
| 447 /** | 460 /** |
| 448 * Merges notification groups into a set of Chrome notifications and shows them. | 461 * Merges notification groups into a set of Chrome notifications and shows them. |
| 449 * @param {Object.<string, StorageGroup>} notificationGroups Map from group name | 462 * @param {Object.<string, StorageGroup>} notificationGroups Map from group name |
| 450 * to group information. | 463 * to group information. |
| 451 */ | 464 */ |
| 452 function mergeAndShowNotificationCards(notificationGroups) { | 465 function mergeAndShowNotificationCards(notificationGroups) { |
| 453 var mergedCards = {}; | 466 var mergedCards = {}; |
| 454 | 467 |
| 455 for (var groupName in notificationGroups) | 468 for (var groupName in notificationGroups) |
| 456 mergeGroup(mergedCards, notificationGroups[groupName]); | 469 mergeGroup(mergedCards, notificationGroups[groupName]); |
| 457 | 470 |
| 458 showNotificationCards(mergedCards); | 471 showNotificationCards(mergedCards); |
| 459 } | 472 } |
| 460 | 473 |
| 461 /** | 474 /** |
| 462 * Parses JSON response from the notification server, shows notifications and | 475 * Parses JSON response from the notification server, shows notifications and |
| 463 * schedules next update. | 476 * schedules next update. |
| 464 * @param {string} response Server response. | 477 * @param {string} response Server response. |
| 465 */ | 478 */ |
| 466 function parseAndShowNotificationCards(response) { | 479 function parseAndShowNotificationCards(response) { |
| 467 console.log('parseAndShowNotificationCards ' + response); | 480 console.log('parseAndShowNotificationCards ' + response); |
| 468 var parsedResponse = JSON.parse(response); | 481 var parsedResponse = JSON.parse(response); |
| 469 | 482 |
| 483 if (parsedResponse.googleNowDisabled) { | |
| 484 chrome.storage.local.set({googleNowEnabled: false}); | |
| 485 // TODO(vadimt): Remove the line below once the server stops sending groups | |
| 486 // with 'googleNowDisabled' responses. | |
| 487 parsedResponse.groups = {}; | |
| 488 } | |
| 489 | |
| 470 var receivedGroups = parsedResponse.groups; | 490 var receivedGroups = parsedResponse.groups; |
| 471 | 491 |
| 472 // Populate groups with corresponding cards. | 492 // Populate groups with corresponding cards. |
| 473 if (parsedResponse.notifications) { | 493 if (parsedResponse.notifications) { |
| 474 for (var i = 0; i != parsedResponse.notifications.length; ++i) { | 494 for (var i = 0; i != parsedResponse.notifications.length; ++i) { |
| 475 var card = parsedResponse.notifications[i]; | 495 var card = parsedResponse.notifications[i]; |
| 476 var group = receivedGroups[card.groupName]; | 496 var group = receivedGroups[card.groupName]; |
| 477 group.cards = group.cards || []; | 497 group.cards = group.cards || []; |
| 478 group.cards.push(card); | 498 group.cards.push(card); |
| 479 } | 499 } |
| (...skipping 80 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 560 } | 580 } |
| 561 }; | 581 }; |
| 562 | 582 |
| 563 setAuthorization(request, function(success) { | 583 setAuthorization(request, function(success) { |
| 564 if (success) | 584 if (success) |
| 565 request.send(); | 585 request.send(); |
| 566 }); | 586 }); |
| 567 } | 587 } |
| 568 | 588 |
| 569 /** | 589 /** |
| 590 * Requests the account opted-in state from the server. | |
| 591 * @param {function()} successCallback Function that will be called if | |
|
skare_
2013/10/31 00:49:21
maybe add param to make it successCallback(bool is
vadimt
2013/10/31 14:36:00
Done.
| |
| 592 * opted-in state is 'true'. | |
| 593 */ | |
| 594 function requestOptedIn(successCallback) { | |
| 595 console.log('requestOptedIn from ' + NOTIFICATION_CARDS_URL); | |
| 596 | |
| 597 var request = buildServerRequest('GET', 'settings/optin'); | |
| 598 | |
| 599 request.onloadend = function(event) { | |
| 600 console.log( | |
| 601 'requestOptedIn-onloadend ' + request.status + ' ' + request.response); | |
| 602 if (request.status == HTTP_OK) { | |
| 603 var parsedResponse = JSON.parse(request.response); | |
| 604 if (parsedResponse.value) { | |
| 605 chrome.storage.local.set({googleNowEnabled: true}); | |
| 606 successCallback(); | |
| 607 } else { | |
| 608 scheduleNextPoll({}); | |
| 609 } | |
| 610 } | |
| 611 }; | |
| 612 | |
| 613 setAuthorization(request, function(success) { | |
| 614 if (success) | |
| 615 request.send(); | |
| 616 }); | |
| 617 } | |
| 618 | |
| 619 /** | |
| 570 * Requests notification cards from the server. | 620 * Requests notification cards from the server. |
| 571 * @param {Location} position Location of this computer. | 621 * @param {Location} position Location of this computer. |
| 572 */ | 622 */ |
| 573 function requestNotificationCards(position) { | 623 function requestNotificationCards(position) { |
| 574 console.log('requestNotificationCards ' + JSON.stringify(position)); | 624 console.log('requestNotificationCards ' + JSON.stringify(position)); |
| 575 | 625 |
| 576 instrumented.storage.local.get('notificationGroups', function(items) { | 626 instrumented.storage.local.get( |
| 627 ['notificationGroups', 'googleNowEnabled'], function(items) { | |
| 577 console.log('requestNotificationCards-storage-get ' + | 628 console.log('requestNotificationCards-storage-get ' + |
| 578 JSON.stringify(items)); | 629 JSON.stringify(items)); |
| 579 items = items || {}; | 630 items = items || {}; |
| 580 | 631 |
| 581 var groupsToRequest = []; | 632 var groupsToRequest = []; |
| 582 | 633 |
| 583 if (items.notificationGroups) { | 634 if (items.notificationGroups) { |
| 584 var now = Date.now(); | 635 var now = Date.now(); |
| 585 | 636 |
| 586 for (var groupName in items.notificationGroups) { | 637 for (var groupName in items.notificationGroups) { |
| 587 var group = items.notificationGroups[groupName]; | 638 var group = items.notificationGroups[groupName]; |
| 588 if (group.nextPollTime !== undefined && group.nextPollTime <= now) | 639 if (group.nextPollTime !== undefined && group.nextPollTime <= now) |
| 589 groupsToRequest.push(groupName); | 640 groupsToRequest.push(groupName); |
| 590 } | 641 } |
| 591 } | 642 } |
| 592 | 643 |
| 593 requestNotificationGroups(groupsToRequest); | 644 if (items.googleNowEnabled) { |
| 645 requestNotificationGroups(groupsToRequest); | |
| 646 } else { | |
| 647 requestOptedIn(function() { | |
| 648 requestNotificationGroups(groupsToRequest); | |
| 649 }); | |
| 650 } | |
| 594 }); | 651 }); |
| 595 } | 652 } |
| 596 | 653 |
| 597 /** | 654 /** |
| 598 * Starts getting location for a cards update. | 655 * Starts getting location for a cards update. |
| 599 */ | 656 */ |
| 600 function requestLocation() { | 657 function requestLocation() { |
| 601 console.log('requestLocation'); | 658 console.log('requestLocation'); |
| 602 recordEvent(GoogleNowEvent.LOCATION_REQUEST); | 659 recordEvent(GoogleNowEvent.LOCATION_REQUEST); |
| 603 // TODO(vadimt): Figure out location request options. | 660 // TODO(vadimt): Figure out location request options. |
| (...skipping 471 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1075 }); | 1132 }); |
| 1076 | 1133 |
| 1077 instrumented.pushMessaging.onMessage.addListener(function(message) { | 1134 instrumented.pushMessaging.onMessage.addListener(function(message) { |
| 1078 // message.payload will be '' when the extension first starts. | 1135 // message.payload will be '' when the extension first starts. |
| 1079 // Each time after signing in, we'll get latest payload for all channels. | 1136 // Each time after signing in, we'll get latest payload for all channels. |
| 1080 // So, we need to poll the server only when the payload is non-empty and has | 1137 // So, we need to poll the server only when the payload is non-empty and has |
| 1081 // changed. | 1138 // changed. |
| 1082 console.log('pushMessaging.onMessage ' + JSON.stringify(message)); | 1139 console.log('pushMessaging.onMessage ' + JSON.stringify(message)); |
| 1083 if (message.payload.indexOf('REQUEST_CARDS') == 0) { | 1140 if (message.payload.indexOf('REQUEST_CARDS') == 0) { |
| 1084 tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() { | 1141 tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() { |
| 1085 instrumented.storage.local.get('lastPollNowPayloads', function(items) { | 1142 instrumented.storage.local.get( |
| 1143 ['lastPollNowPayloads', 'notificationGroups'], function(items) { | |
| 1086 // If storage.get fails, it's safer to do nothing, preventing polling | 1144 // If storage.get fails, it's safer to do nothing, preventing polling |
| 1087 // the server when the payload really didn't change. | 1145 // the server when the payload really didn't change. |
| 1088 if (!items) | 1146 if (!items) |
| 1089 return; | 1147 return; |
| 1090 | 1148 |
| 1091 // If this is the first time we get lastPollNowPayloads, initialize it. | 1149 // If this is the first time we get lastPollNowPayloads, initialize it. |
| 1092 items.lastPollNowPayloads = items.lastPollNowPayloads || {}; | 1150 items.lastPollNowPayloads = items.lastPollNowPayloads || {}; |
| 1093 | 1151 |
| 1094 if (items.lastPollNowPayloads[message.subchannelId] != | 1152 if (items.lastPollNowPayloads[message.subchannelId] != |
| 1095 message.payload) { | 1153 message.payload) { |
| 1096 items.lastPollNowPayloads[message.subchannelId] = message.payload; | 1154 items.lastPollNowPayloads[message.subchannelId] = message.payload; |
| 1097 chrome.storage.local.set( | |
| 1098 {lastPollNowPayloads: items.lastPollNowPayloads}); | |
| 1099 | 1155 |
| 1100 updateCardsAttempts.isRunning(function(running) { | 1156 items.notificationGroups = items.notificationGroups || {}; |
| 1101 if (running) | 1157 items.notificationGroups['PUSH' + message.subchannelId] = { |
| 1102 requestNotificationGroups(['PUSH' + message.subchannelId]); | 1158 cards: [], |
| 1159 nextPollTime: Date.now() | |
| 1160 }; | |
| 1161 | |
| 1162 chrome.storage.local.set({ | |
| 1163 lastPollNowPayloads: items.lastPollNowPayloads, | |
| 1164 notificationGroups: items.notificationGroups | |
| 1103 }); | 1165 }); |
| 1166 | |
| 1167 updateNotificationsCards(); | |
| 1104 } | 1168 } |
| 1105 }); | 1169 }); |
| 1106 }); | 1170 }); |
| 1107 } | 1171 } |
| 1108 }); | 1172 }); |
| OLD | NEW |