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 329 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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. |
| 431 * @param {boolean} isOptedIn True if the user is opted in to Google Now. |
426 */ | 432 */ |
427 function scheduleNextPoll(groups) { | 433 function scheduleNextPoll(groups, isOptedIn) { |
428 var nextPollTime = null; | 434 if (isOptedIn) { |
| 435 var nextPollTime = null; |
429 | 436 |
430 for (var groupName in groups) { | 437 for (var groupName in groups) { |
431 var group = groups[groupName]; | 438 var group = groups[groupName]; |
432 if (group.nextPollTime !== undefined) { | 439 if (group.nextPollTime !== undefined) { |
433 nextPollTime = nextPollTime == null ? | 440 nextPollTime = nextPollTime == null ? |
434 group.nextPollTime : Math.min(group.nextPollTime, nextPollTime); | 441 group.nextPollTime : Math.min(group.nextPollTime, nextPollTime); |
| 442 } |
435 } | 443 } |
| 444 |
| 445 // At least one of the groups must have nextPollTime. |
| 446 verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null'); |
| 447 |
| 448 var nextPollDelaySeconds = Math.max( |
| 449 (nextPollTime - Date.now()) / MS_IN_SECOND, |
| 450 MINIMUM_POLLING_PERIOD_SECONDS); |
| 451 updateCardsAttempts.start(nextPollDelaySeconds); |
| 452 } else { |
| 453 instrumented.metricsPrivate.getVariationParams( |
| 454 'GoogleNow', function(params) { |
| 455 var optinPollPeriodSeconds = |
| 456 parseInt(params && params.optinPollPeriodSeconds, 10) || |
| 457 DEFAULT_OPTIN_CHECK_PERIOD_SECONDS; |
| 458 updateCardsAttempts.start(optinPollPeriodSeconds); |
| 459 }); |
436 } | 460 } |
437 | |
438 // At least one of the groups must have nextPollTime. | |
439 verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null'); | |
440 | |
441 var nextPollDelaySeconds = Math.max( | |
442 (nextPollTime - Date.now()) / MS_IN_SECOND, | |
443 MINIMUM_POLLING_PERIOD_SECONDS); | |
444 updateCardsAttempts.start(nextPollDelaySeconds); | |
445 } | 461 } |
446 | 462 |
447 /** | 463 /** |
448 * Merges notification groups into a set of Chrome notifications and shows them. | 464 * Merges notification groups into a set of Chrome notifications and shows them. |
449 * @param {Object.<string, StorageGroup>} notificationGroups Map from group name | 465 * @param {Object.<string, StorageGroup>} notificationGroups Map from group name |
450 * to group information. | 466 * to group information. |
451 */ | 467 */ |
452 function mergeAndShowNotificationCards(notificationGroups) { | 468 function mergeAndShowNotificationCards(notificationGroups) { |
453 var mergedCards = {}; | 469 var mergedCards = {}; |
454 | 470 |
455 for (var groupName in notificationGroups) | 471 for (var groupName in notificationGroups) |
456 mergeGroup(mergedCards, notificationGroups[groupName]); | 472 mergeGroup(mergedCards, notificationGroups[groupName]); |
457 | 473 |
458 showNotificationCards(mergedCards); | 474 showNotificationCards(mergedCards); |
459 } | 475 } |
460 | 476 |
461 /** | 477 /** |
462 * Parses JSON response from the notification server, shows notifications and | 478 * Parses JSON response from the notification server, shows notifications and |
463 * schedules next update. | 479 * schedules next update. |
464 * @param {string} response Server response. | 480 * @param {string} response Server response. |
465 */ | 481 */ |
466 function parseAndShowNotificationCards(response) { | 482 function parseAndShowNotificationCards(response) { |
467 console.log('parseAndShowNotificationCards ' + response); | 483 console.log('parseAndShowNotificationCards ' + response); |
468 var parsedResponse = JSON.parse(response); | 484 var parsedResponse = JSON.parse(response); |
469 | 485 |
| 486 if (parsedResponse.googleNowDisabled) { |
| 487 chrome.storage.local.set({googleNowEnabled: false}); |
| 488 // TODO(vadimt): Remove the line below once the server stops sending groups |
| 489 // with 'googleNowDisabled' responses. |
| 490 parsedResponse.groups = {}; |
| 491 } |
| 492 |
470 var receivedGroups = parsedResponse.groups; | 493 var receivedGroups = parsedResponse.groups; |
471 | 494 |
472 // Populate groups with corresponding cards. | 495 // Populate groups with corresponding cards. |
473 if (parsedResponse.notifications) { | 496 if (parsedResponse.notifications) { |
474 for (var i = 0; i != parsedResponse.notifications.length; ++i) { | 497 for (var i = 0; i != parsedResponse.notifications.length; ++i) { |
475 var card = parsedResponse.notifications[i]; | 498 var card = parsedResponse.notifications[i]; |
476 var group = receivedGroups[card.groupName]; | 499 var group = receivedGroups[card.groupName]; |
477 group.cards = group.cards || []; | 500 group.cards = group.cards || []; |
478 group.cards.push(card); | 501 group.cards.push(card); |
479 } | 502 } |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
517 // 'nextPollSeconds' may be sent even for groups that don't contain cards | 540 // 'nextPollSeconds' may be sent even for groups that don't contain cards |
518 // updates. | 541 // updates. |
519 if (receivedGroup.nextPollSeconds !== undefined) { | 542 if (receivedGroup.nextPollSeconds !== undefined) { |
520 storageGroup.nextPollTime = | 543 storageGroup.nextPollTime = |
521 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; | 544 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; |
522 } | 545 } |
523 | 546 |
524 updatedGroups[groupName] = storageGroup; | 547 updatedGroups[groupName] = storageGroup; |
525 } | 548 } |
526 | 549 |
527 scheduleNextPoll(updatedGroups); | 550 scheduleNextPoll(updatedGroups, !parsedResponse.googleNowDisabled); |
528 chrome.storage.local.set({notificationGroups: updatedGroups}); | 551 chrome.storage.local.set({notificationGroups: updatedGroups}); |
529 mergeAndShowNotificationCards(updatedGroups); | 552 mergeAndShowNotificationCards(updatedGroups); |
530 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); | 553 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); |
531 }); | 554 }); |
532 } | 555 } |
533 | 556 |
534 /** | 557 /** |
535 * Requests notification cards from the server for specified groups. | 558 * Requests notification cards from the server for specified groups. |
536 * @param {Array.<string>} groupNames Names of groups that need to be refreshed. | 559 * @param {Array.<string>} groupNames Names of groups that need to be refreshed. |
537 */ | 560 */ |
(...skipping 22 matching lines...) Expand all Loading... |
560 } | 583 } |
561 }; | 584 }; |
562 | 585 |
563 setAuthorization(request, function(success) { | 586 setAuthorization(request, function(success) { |
564 if (success) | 587 if (success) |
565 request.send(); | 588 request.send(); |
566 }); | 589 }); |
567 } | 590 } |
568 | 591 |
569 /** | 592 /** |
| 593 * Requests the account opted-in state from the server. |
| 594 * @param {function()} optedInCallback Function that will be called if |
| 595 * opted-in state is 'true'. |
| 596 */ |
| 597 function requestOptedIn(optedInCallback) { |
| 598 console.log('requestOptedIn from ' + NOTIFICATION_CARDS_URL); |
| 599 |
| 600 var request = buildServerRequest('GET', 'settings/optin'); |
| 601 |
| 602 request.onloadend = function(event) { |
| 603 console.log( |
| 604 'requestOptedIn-onloadend ' + request.status + ' ' + request.response); |
| 605 if (request.status == HTTP_OK) { |
| 606 var parsedResponse = JSON.parse(request.response); |
| 607 if (parsedResponse.value) { |
| 608 chrome.storage.local.set({googleNowEnabled: true}); |
| 609 optedInCallback(); |
| 610 } else { |
| 611 scheduleNextPoll({}, false); |
| 612 } |
| 613 } |
| 614 }; |
| 615 |
| 616 setAuthorization(request, function(success) { |
| 617 if (success) |
| 618 request.send(); |
| 619 }); |
| 620 } |
| 621 |
| 622 /** |
570 * Requests notification cards from the server. | 623 * Requests notification cards from the server. |
571 * @param {Location} position Location of this computer. | 624 * @param {Location} position Location of this computer. |
572 */ | 625 */ |
573 function requestNotificationCards(position) { | 626 function requestNotificationCards(position) { |
574 console.log('requestNotificationCards ' + JSON.stringify(position)); | 627 console.log('requestNotificationCards ' + JSON.stringify(position)); |
575 | 628 |
576 instrumented.storage.local.get('notificationGroups', function(items) { | 629 instrumented.storage.local.get( |
| 630 ['notificationGroups', 'googleNowEnabled'], function(items) { |
577 console.log('requestNotificationCards-storage-get ' + | 631 console.log('requestNotificationCards-storage-get ' + |
578 JSON.stringify(items)); | 632 JSON.stringify(items)); |
579 items = items || {}; | 633 items = items || {}; |
580 | 634 |
581 var groupsToRequest = []; | 635 var groupsToRequest = []; |
582 | 636 |
583 if (items.notificationGroups) { | 637 if (items.notificationGroups) { |
584 var now = Date.now(); | 638 var now = Date.now(); |
585 | 639 |
586 for (var groupName in items.notificationGroups) { | 640 for (var groupName in items.notificationGroups) { |
587 var group = items.notificationGroups[groupName]; | 641 var group = items.notificationGroups[groupName]; |
588 if (group.nextPollTime !== undefined && group.nextPollTime <= now) | 642 if (group.nextPollTime !== undefined && group.nextPollTime <= now) |
589 groupsToRequest.push(groupName); | 643 groupsToRequest.push(groupName); |
590 } | 644 } |
591 } | 645 } |
592 | 646 |
593 requestNotificationGroups(groupsToRequest); | 647 if (items.googleNowEnabled) { |
| 648 requestNotificationGroups(groupsToRequest); |
| 649 } else { |
| 650 requestOptedIn(function() { |
| 651 requestNotificationGroups(groupsToRequest); |
| 652 }); |
| 653 } |
594 }); | 654 }); |
595 } | 655 } |
596 | 656 |
597 /** | 657 /** |
598 * Starts getting location for a cards update. | 658 * Starts getting location for a cards update. |
599 */ | 659 */ |
600 function requestLocation() { | 660 function requestLocation() { |
601 console.log('requestLocation'); | 661 console.log('requestLocation'); |
602 recordEvent(GoogleNowEvent.LOCATION_REQUEST); | 662 recordEvent(GoogleNowEvent.LOCATION_REQUEST); |
603 // TODO(vadimt): Figure out location request options. | 663 // TODO(vadimt): Figure out location request options. |
(...skipping 471 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1075 }); | 1135 }); |
1076 | 1136 |
1077 instrumented.pushMessaging.onMessage.addListener(function(message) { | 1137 instrumented.pushMessaging.onMessage.addListener(function(message) { |
1078 // message.payload will be '' when the extension first starts. | 1138 // message.payload will be '' when the extension first starts. |
1079 // Each time after signing in, we'll get latest payload for all channels. | 1139 // 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 | 1140 // So, we need to poll the server only when the payload is non-empty and has |
1081 // changed. | 1141 // changed. |
1082 console.log('pushMessaging.onMessage ' + JSON.stringify(message)); | 1142 console.log('pushMessaging.onMessage ' + JSON.stringify(message)); |
1083 if (message.payload.indexOf('REQUEST_CARDS') == 0) { | 1143 if (message.payload.indexOf('REQUEST_CARDS') == 0) { |
1084 tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() { | 1144 tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() { |
1085 instrumented.storage.local.get('lastPollNowPayloads', function(items) { | 1145 instrumented.storage.local.get( |
| 1146 ['lastPollNowPayloads', 'notificationGroups'], function(items) { |
1086 // If storage.get fails, it's safer to do nothing, preventing polling | 1147 // If storage.get fails, it's safer to do nothing, preventing polling |
1087 // the server when the payload really didn't change. | 1148 // the server when the payload really didn't change. |
1088 if (!items) | 1149 if (!items) |
1089 return; | 1150 return; |
1090 | 1151 |
1091 // If this is the first time we get lastPollNowPayloads, initialize it. | 1152 // If this is the first time we get lastPollNowPayloads, initialize it. |
1092 items.lastPollNowPayloads = items.lastPollNowPayloads || {}; | 1153 items.lastPollNowPayloads = items.lastPollNowPayloads || {}; |
1093 | 1154 |
1094 if (items.lastPollNowPayloads[message.subchannelId] != | 1155 if (items.lastPollNowPayloads[message.subchannelId] != |
1095 message.payload) { | 1156 message.payload) { |
1096 items.lastPollNowPayloads[message.subchannelId] = message.payload; | 1157 items.lastPollNowPayloads[message.subchannelId] = message.payload; |
1097 chrome.storage.local.set( | |
1098 {lastPollNowPayloads: items.lastPollNowPayloads}); | |
1099 | 1158 |
1100 updateCardsAttempts.isRunning(function(running) { | 1159 items.notificationGroups = items.notificationGroups || {}; |
1101 if (running) | 1160 items.notificationGroups['PUSH' + message.subchannelId] = { |
1102 requestNotificationGroups(['PUSH' + message.subchannelId]); | 1161 cards: [], |
| 1162 nextPollTime: Date.now() |
| 1163 }; |
| 1164 |
| 1165 chrome.storage.local.set({ |
| 1166 lastPollNowPayloads: items.lastPollNowPayloads, |
| 1167 notificationGroups: items.notificationGroups |
1103 }); | 1168 }); |
| 1169 |
| 1170 updateNotificationsCards(); |
1104 } | 1171 } |
1105 }); | 1172 }); |
1106 }); | 1173 }); |
1107 } | 1174 } |
1108 }); | 1175 }); |
OLD | NEW |