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 |