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 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
47 /** | 47 /** |
48 * Maximal period for polling for Google Now Notifications cards to use when the | 48 * Maximal period for polling for Google Now Notifications cards to use when the |
49 * period from the server is not available. | 49 * period from the server is not available. |
50 */ | 50 */ |
51 var MAXIMUM_POLLING_PERIOD_SECONDS = 60 * 60; // 1 hour | 51 var MAXIMUM_POLLING_PERIOD_SECONDS = 60 * 60; // 1 hour |
52 | 52 |
53 /** | 53 /** |
54 * Initial period for polling for Google Now optin notification after push | 54 * Initial period for polling for Google Now optin notification after push |
55 * messaging indicates Google Now is enabled. | 55 * messaging indicates Google Now is enabled. |
56 */ | 56 */ |
57 var INITIAL_OPTIN_POLLING_PERIOD_SECONDS = 60; // 1 minute | 57 var INITIAL_OPTIN_RECHECK_PERIOD_SECONDS = 60; // 1 minute |
58 | 58 |
59 /** | 59 /** |
60 * Maximum period for polling for Google Now optin notification after push | 60 * Maximum period for polling for Google Now optin notification after push |
61 * messaging indicates Google Now is enabled. It is expected that the alarm | 61 * messaging indicates Google Now is enabled. It is expected that the alarm |
62 * will be stopped after this. | 62 * will be stopped after this. |
63 */ | 63 */ |
64 var MAXIMUM_OPTIN_POLLING_PERIOD_SECONDS = 16 * 60; // 16 minutes | 64 var MAXIMUM_OPTIN_RECHECK_PERIOD_SECONDS = 16 * 60; // 16 minutes |
65 | 65 |
66 /** | 66 /** |
67 * Initial period for retrying the server request for dismissing cards. | 67 * Initial period for retrying the server request for dismissing cards. |
68 */ | 68 */ |
69 var INITIAL_RETRY_DISMISS_PERIOD_SECONDS = 60; // 1 minute | 69 var INITIAL_RETRY_DISMISS_PERIOD_SECONDS = 60; // 1 minute |
70 | 70 |
71 /** | 71 /** |
72 * Maximum period for retrying the server request for dismissing cards. | 72 * Maximum period for retrying the server request for dismissing cards. |
73 */ | 73 */ |
74 var MAXIMUM_RETRY_DISMISS_PERIOD_SECONDS = 60 * 60; // 1 hour | 74 var MAXIMUM_RETRY_DISMISS_PERIOD_SECONDS = 60 * 60; // 1 hour |
(...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
198 wrapper.instrumentChromeApiFunction( | 198 wrapper.instrumentChromeApiFunction( |
199 'notifications.onButtonClicked.addListener', 0); | 199 'notifications.onButtonClicked.addListener', 0); |
200 wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0); | 200 wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0); |
201 wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0); | 201 wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0); |
202 wrapper.instrumentChromeApiFunction( | 202 wrapper.instrumentChromeApiFunction( |
203 'notifications.onPermissionLevelChanged.addListener', 0); | 203 'notifications.onPermissionLevelChanged.addListener', 0); |
204 wrapper.instrumentChromeApiFunction( | 204 wrapper.instrumentChromeApiFunction( |
205 'notifications.onShowSettings.addListener', 0); | 205 'notifications.onShowSettings.addListener', 0); |
206 wrapper.instrumentChromeApiFunction('permissions.contains', 1); | 206 wrapper.instrumentChromeApiFunction('permissions.contains', 1); |
207 wrapper.instrumentChromeApiFunction('pushMessaging.onMessage.addListener', 0); | 207 wrapper.instrumentChromeApiFunction('pushMessaging.onMessage.addListener', 0); |
| 208 wrapper.instrumentChromeApiFunction('storage.onChanged.addListener', 0); |
208 wrapper.instrumentChromeApiFunction('runtime.onInstalled.addListener', 0); | 209 wrapper.instrumentChromeApiFunction('runtime.onInstalled.addListener', 0); |
209 wrapper.instrumentChromeApiFunction('runtime.onStartup.addListener', 0); | 210 wrapper.instrumentChromeApiFunction('runtime.onStartup.addListener', 0); |
210 wrapper.instrumentChromeApiFunction('tabs.create', 1); | 211 wrapper.instrumentChromeApiFunction('tabs.create', 1); |
211 | 212 |
212 var updateCardsAttempts = buildAttemptManager( | 213 var updateCardsAttempts = buildAttemptManager( |
213 'cards-update', | 214 'cards-update', |
214 requestCards, | 215 requestCards, |
215 INITIAL_POLLING_PERIOD_SECONDS, | 216 INITIAL_POLLING_PERIOD_SECONDS, |
216 MAXIMUM_POLLING_PERIOD_SECONDS); | 217 MAXIMUM_POLLING_PERIOD_SECONDS); |
217 var optInCheckAttempts = buildAttemptManager( | 218 var optInPollAttempts = buildAttemptManager( |
218 'optin', | 219 'optin', |
219 pollOptedIn, | 220 pollOptedInNoImmediateRecheck, |
220 INITIAL_OPTIN_POLLING_PERIOD_SECONDS, | 221 INITIAL_POLLING_PERIOD_SECONDS, |
221 MAXIMUM_OPTIN_POLLING_PERIOD_SECONDS); | 222 MAXIMUM_POLLING_PERIOD_SECONDS); |
| 223 var optInRecheckAttempts = buildAttemptManager( |
| 224 'optin-recheck', |
| 225 pollOptedInWithRecheck, |
| 226 INITIAL_OPTIN_RECHECK_PERIOD_SECONDS, |
| 227 MAXIMUM_OPTIN_RECHECK_PERIOD_SECONDS); |
222 var dismissalAttempts = buildAttemptManager( | 228 var dismissalAttempts = buildAttemptManager( |
223 'dismiss', | 229 'dismiss', |
224 retryPendingDismissals, | 230 retryPendingDismissals, |
225 INITIAL_RETRY_DISMISS_PERIOD_SECONDS, | 231 INITIAL_RETRY_DISMISS_PERIOD_SECONDS, |
226 MAXIMUM_RETRY_DISMISS_PERIOD_SECONDS); | 232 MAXIMUM_RETRY_DISMISS_PERIOD_SECONDS); |
227 var cardSet = buildCardSet(); | 233 var cardSet = buildCardSet(); |
228 | 234 |
229 var authenticationManager = buildAuthenticationManager(); | 235 var authenticationManager = buildAuthenticationManager(); |
230 | 236 |
231 /** | 237 /** |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 }; | 424 }; |
419 | 425 |
420 var combinedCard = | 426 var combinedCard = |
421 combinedCards[receivedNotification.chromeNotificationId] || []; | 427 combinedCards[receivedNotification.chromeNotificationId] || []; |
422 combinedCard.push(uncombinedNotification); | 428 combinedCard.push(uncombinedNotification); |
423 combinedCards[receivedNotification.chromeNotificationId] = combinedCard; | 429 combinedCards[receivedNotification.chromeNotificationId] = combinedCard; |
424 } | 430 } |
425 } | 431 } |
426 | 432 |
427 /** | 433 /** |
| 434 * Calculates the soonest poll time from a map of groups as an absolute time. |
| 435 * @param {Object.<string, StoredNotificationGroup>} groups Map from group name |
| 436 * to group information. |
| 437 * @return {number} The next poll time based off of the groups. |
| 438 */ |
| 439 function calculateNextPollTimeMilliseconds(groups) { |
| 440 var nextPollTime = null; |
| 441 |
| 442 for (var groupName in groups) { |
| 443 var group = groups[groupName]; |
| 444 if (group.nextPollTime !== undefined) { |
| 445 nextPollTime = nextPollTime == null ? |
| 446 group.nextPollTime : Math.min(group.nextPollTime, nextPollTime); |
| 447 } |
| 448 } |
| 449 |
| 450 // At least one of the groups must have nextPollTime. |
| 451 verify(nextPollTime != null, 'calculateNextPollTime: nextPollTime is null'); |
| 452 return nextPollTime; |
| 453 } |
| 454 |
| 455 /** |
428 * Schedules next cards poll. | 456 * Schedules next cards poll. |
429 * @param {Object.<string, StoredNotificationGroup>} groups Map from group name | 457 * @param {Object.<string, StoredNotificationGroup>} groups Map from group name |
430 * to group information. | 458 * to group information. |
431 * @param {boolean} isOptedIn True if the user is opted in to Google Now. | |
432 */ | 459 */ |
433 function scheduleNextPoll(groups, isOptedIn) { | 460 function scheduleNextCardsPoll(groups) { |
434 if (isOptedIn) { | 461 var nextPollTimeMs = calculateNextPollTimeMilliseconds(groups); |
435 var nextPollTime = null; | |
436 | 462 |
437 for (var groupName in groups) { | 463 var nextPollDelaySeconds = Math.max( |
438 var group = groups[groupName]; | 464 (nextPollTimeMs - Date.now()) / MS_IN_SECOND, |
439 if (group.nextPollTime !== undefined) { | 465 MINIMUM_POLLING_PERIOD_SECONDS); |
440 nextPollTime = nextPollTime == null ? | 466 updateCardsAttempts.start(nextPollDelaySeconds); |
441 group.nextPollTime : Math.min(group.nextPollTime, nextPollTime); | |
442 } | |
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 }); | |
460 } | |
461 } | 467 } |
462 | 468 |
463 /** | 469 /** |
| 470 * Schedules the next opt-in check poll. |
| 471 */ |
| 472 function scheduleOptInCheckPoll() { |
| 473 instrumented.metricsPrivate.getVariationParams( |
| 474 'GoogleNow', function(params) { |
| 475 var optinPollPeriodSeconds = |
| 476 parseInt(params && params.optinPollPeriodSeconds, 10) || |
| 477 DEFAULT_OPTIN_CHECK_PERIOD_SECONDS; |
| 478 optInPollAttempts.start(optinPollPeriodSeconds); |
| 479 }); |
| 480 } |
| 481 |
| 482 /** |
464 * Combines notification groups into a set of Chrome notifications. | 483 * Combines notification groups into a set of Chrome notifications. |
465 * @param {Object.<string, StoredNotificationGroup>} notificationGroups Map from | 484 * @param {Object.<string, StoredNotificationGroup>} notificationGroups Map from |
466 * group name to group information. | 485 * group name to group information. |
467 * @return {Object.<ChromeNotificationId, CombinedCard>} Cards to show. | 486 * @return {Object.<ChromeNotificationId, CombinedCard>} Cards to show. |
468 */ | 487 */ |
469 function combineCardsFromGroups(notificationGroups) { | 488 function combineCardsFromGroups(notificationGroups) { |
470 console.log('combineCardsFromGroups ' + JSON.stringify(notificationGroups)); | 489 console.log('combineCardsFromGroups ' + JSON.stringify(notificationGroups)); |
471 /** @type {Object.<ChromeNotificationId, CombinedCard>} */ | 490 /** @type {Object.<ChromeNotificationId, CombinedCard>} */ |
472 var combinedCards = {}; | 491 var combinedCards = {}; |
473 | 492 |
474 for (var groupName in notificationGroups) | 493 for (var groupName in notificationGroups) |
475 combineGroup(combinedCards, notificationGroups[groupName]); | 494 combineGroup(combinedCards, notificationGroups[groupName]); |
476 | 495 |
477 return combinedCards; | 496 return combinedCards; |
478 } | 497 } |
479 | 498 |
480 /** | 499 /** |
481 * Processes a server response for consumption by showNotificationGroups. | 500 * Processes a server response for consumption by showNotificationGroups. |
482 * @param {ServerResponse} response Server response. | 501 * @param {ServerResponse} response Server response. |
483 * @return {Promise} A promise to process the server response and provide | 502 * @return {Promise} A promise to process the server response and provide |
484 * updated groups. Rejects if the server response shouldn't be processed. | 503 * updated groups. Rejects if the server response shouldn't be processed. |
485 */ | 504 */ |
486 function processServerResponse(response) { | 505 function processServerResponse(response) { |
487 console.log('processServerResponse ' + JSON.stringify(response)); | 506 console.log('processServerResponse ' + JSON.stringify(response)); |
488 | 507 |
489 if (response.googleNowDisabled) { | 508 if (response.googleNowDisabled) { |
490 chrome.storage.local.set({googleNowEnabled: false}); | 509 chrome.storage.local.set({googleNowEnabled: false}); |
491 // TODO(robliao): Remove the line below once the server stops sending groups | |
492 // with 'googleNowDisabled' responses. | |
493 response.groups = {}; | |
494 // Google Now was enabled; now it's disabled. This is a state change. | |
495 onStateChange(); | |
496 // Start the Google Now Disabled polling period. | |
497 scheduleNextPoll({}, false); | |
498 // Stop processing now. The state change will clear the cards. | 510 // Stop processing now. The state change will clear the cards. |
499 return Promise.reject(); | 511 return Promise.reject(); |
500 } | 512 } |
501 | 513 |
502 var receivedGroups = response.groups; | 514 var receivedGroups = response.groups; |
503 | 515 |
504 return fillFromChromeLocalStorage({ | 516 return fillFromChromeLocalStorage({ |
505 /** @type {Object.<string, StoredNotificationGroup>} */ | 517 /** @type {Object.<string, StoredNotificationGroup>} */ |
506 notificationGroups: {}, | 518 notificationGroups: {}, |
507 /** @type {Object.<ServerNotificationId, number>} */ | 519 /** @type {Object.<ServerNotificationId, number>} */ |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
566 // 'nextPollSeconds' may be sent even for groups that don't contain | 578 // 'nextPollSeconds' may be sent even for groups that don't contain |
567 // cards updates. | 579 // cards updates. |
568 if (receivedGroup.nextPollSeconds !== undefined) { | 580 if (receivedGroup.nextPollSeconds !== undefined) { |
569 storedGroup.nextPollTime = | 581 storedGroup.nextPollTime = |
570 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; | 582 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; |
571 } | 583 } |
572 | 584 |
573 updatedGroups[groupName] = storedGroup; | 585 updatedGroups[groupName] = storedGroup; |
574 } | 586 } |
575 | 587 |
576 scheduleNextPoll(updatedGroups, !response.googleNowDisabled); | 588 scheduleNextCardsPoll(updatedGroups); |
577 return { | 589 return { |
578 updatedGroups: updatedGroups, | 590 updatedGroups: updatedGroups, |
579 recentDismissals: updatedRecentDismissals | 591 recentDismissals: updatedRecentDismissals |
580 }; | 592 }; |
581 }); | 593 }); |
582 } | 594 } |
583 | 595 |
584 /** | 596 /** |
585 * Update the Explanatory Total Cards Shown Count. | 597 * Update the Explanatory Total Cards Shown Count. |
586 */ | 598 */ |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
631 console.log( | 643 console.log( |
632 'requestNotificationGroupsFromServer-received ' + request.status); | 644 'requestNotificationGroupsFromServer-received ' + request.status); |
633 if (request.status == HTTP_OK) { | 645 if (request.status == HTTP_OK) { |
634 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); | 646 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); |
635 return JSON.parse(request.responseText); | 647 return JSON.parse(request.responseText); |
636 } | 648 } |
637 }); | 649 }); |
638 } | 650 } |
639 | 651 |
640 /** | 652 /** |
| 653 * Performs an opt-in poll without an immediate recheck. |
| 654 * If the response is not opted-in, schedule an opt-in check poll. |
| 655 */ |
| 656 function pollOptedInNoImmediateRecheck() { |
| 657 requestAndUpdateOptedIn() |
| 658 .then(function(optedIn) { |
| 659 if (!optedIn) { |
| 660 // Request a repoll if we're not opted in. |
| 661 return Promise.reject(); |
| 662 } |
| 663 }) |
| 664 .catch(function() { |
| 665 scheduleOptInCheckPoll(); |
| 666 }); |
| 667 } |
| 668 |
| 669 /** |
641 * Requests the account opted-in state from the server and updates any | 670 * Requests the account opted-in state from the server and updates any |
642 * state as necessary. | 671 * state as necessary. |
643 * @return {Promise} A promise to request and update the opted-in state. | 672 * @return {Promise} A promise to request and update the opted-in state. |
644 * The promise resolves if the opt-in state is true. | 673 * The promise resolves with the opt-in state. |
645 */ | 674 */ |
646 function requestAndUpdateOptedIn() { | 675 function requestAndUpdateOptedIn() { |
647 console.log('requestOptedIn from ' + NOTIFICATION_CARDS_URL); | 676 console.log('requestOptedIn from ' + NOTIFICATION_CARDS_URL); |
648 | 677 |
649 return requestFromServer('GET', 'settings/optin').then(function(request) { | 678 return requestFromServer('GET', 'settings/optin').then(function(request) { |
650 console.log( | 679 console.log( |
651 'requestOptedIn-received ' + request.status + ' ' + request.response); | 680 'requestOptedIn-received ' + request.status + ' ' + request.response); |
652 if (request.status == HTTP_OK) { | 681 if (request.status == HTTP_OK) { |
653 var parsedResponse = JSON.parse(request.responseText); | 682 var parsedResponse = JSON.parse(request.responseText); |
654 return parsedResponse.value; | 683 return parsedResponse.value; |
655 } | 684 } |
656 }).then(function(optedIn) { | 685 }).then(function(optedIn) { |
657 if (optedIn) { | 686 chrome.storage.local.set({googleNowEnabled: optedIn}); |
658 chrome.storage.local.set({googleNowEnabled: true}); | 687 return optedIn; |
659 // Google Now was disabled, now it's enabled. This is a state change. | |
660 onStateChange(); | |
661 return Promise.resolve(); | |
662 } else { | |
663 scheduleNextPoll({}, false); | |
664 return Promise.reject(); | |
665 } | |
666 }); | 688 }); |
667 } | 689 } |
668 | 690 |
669 /** | 691 /** |
670 * Determines the groups that need to be requested right now. | 692 * Determines the groups that need to be requested right now. |
671 * @return {Promise} A promise to determine the groups to request. | 693 * @return {Promise} A promise to determine the groups to request. |
672 */ | 694 */ |
673 function getGroupsToRequest() { | 695 function getGroupsToRequest() { |
674 return fillFromChromeLocalStorage({ | 696 return fillFromChromeLocalStorage({ |
675 /** @type {Object.<string, StoredNotificationGroup>} */ | 697 /** @type {Object.<string, StoredNotificationGroup>} */ |
(...skipping 12 matching lines...) Expand all Loading... |
688 }); | 710 }); |
689 } | 711 } |
690 | 712 |
691 /** | 713 /** |
692 * Requests notification cards from the server. | 714 * Requests notification cards from the server. |
693 * @return {Promise} A promise to request the notification cards. | 715 * @return {Promise} A promise to request the notification cards. |
694 * Rejects if the cards won't be requested. | 716 * Rejects if the cards won't be requested. |
695 */ | 717 */ |
696 function requestNotificationCards() { | 718 function requestNotificationCards() { |
697 console.log('requestNotificationCards'); | 719 console.log('requestNotificationCards'); |
698 | 720 return getGroupsToRequest() |
699 return isGoogleNowEnabled() | |
700 .then(function(googleNowEnabled) { | |
701 return googleNowEnabled ? Promise.resolve() : requestAndUpdateOptedIn(); | |
702 }) | |
703 .then(getGroupsToRequest) | |
704 .then(requestNotificationGroupsFromServer) | 721 .then(requestNotificationGroupsFromServer) |
705 .then(processServerResponse) | 722 .then(processServerResponse) |
706 .then(function(processedResponse) { | 723 .then(function(processedResponse) { |
707 var onCardShown = | 724 var onCardShown = |
708 shouldShowExplanatoryCard() ? countExplanatoryCard : undefined; | 725 shouldShowExplanatoryCard() ? countExplanatoryCard : undefined; |
709 return showNotificationGroups( | 726 return showNotificationGroups( |
710 processedResponse.updatedGroups, onCardShown).then(function() { | 727 processedResponse.updatedGroups, onCardShown).then(function() { |
711 chrome.storage.local.set({ | 728 chrome.storage.local.set({ |
712 notificationGroups: processedResponse.updatedGroups, | 729 notificationGroups: processedResponse.updatedGroups, |
713 recentDismissals: processedResponse.updatedRecentDismissals | 730 recentDismissals: processedResponse.updatedRecentDismissals |
(...skipping 10 matching lines...) Expand all Loading... |
724 function requestCards() { | 741 function requestCards() { |
725 console.log('requestCards @' + new Date()); | 742 console.log('requestCards @' + new Date()); |
726 // LOCATION_REQUEST is a legacy histogram value when we requested location. | 743 // LOCATION_REQUEST is a legacy histogram value when we requested location. |
727 // This corresponds to the extension attempting to request for cards. | 744 // This corresponds to the extension attempting to request for cards. |
728 // We're keeping the name the same to keep our histograms in order. | 745 // We're keeping the name the same to keep our histograms in order. |
729 recordEvent(GoogleNowEvent.LOCATION_REQUEST); | 746 recordEvent(GoogleNowEvent.LOCATION_REQUEST); |
730 tasks.add(UPDATE_CARDS_TASK_NAME, function() { | 747 tasks.add(UPDATE_CARDS_TASK_NAME, function() { |
731 console.log('requestCards-task-begin'); | 748 console.log('requestCards-task-begin'); |
732 updateCardsAttempts.isRunning(function(running) { | 749 updateCardsAttempts.isRunning(function(running) { |
733 if (running) { | 750 if (running) { |
734 updateCardsAttempts.planForNext(function() { | 751 // The cards are requested only if there are no unsent dismissals. |
735 // The cards are requested only if there are no unsent dismissals. | 752 processPendingDismissals() |
736 processPendingDismissals().then(requestNotificationCards); | 753 .then(requestNotificationCards) |
737 }); | 754 .catch(updateCardsAttempts.scheduleRetry); |
738 } | 755 } |
739 }); | 756 }); |
740 }); | 757 }); |
741 } | 758 } |
742 | 759 |
743 /** | 760 /** |
744 * Sends a server request to dismiss a card. | 761 * Sends a server request to dismiss a card. |
745 * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of | 762 * @param {ChromeNotificationId} chromeNotificationId chrome.notifications ID of |
746 * the card. | 763 * the card. |
747 * @param {number} dismissalTimeMs Time of the user's dismissal of the card in | 764 * @param {number} dismissalTimeMs Time of the user's dismissal of the card in |
(...skipping 96 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
844 | 861 |
845 return doProcessDismissals(); | 862 return doProcessDismissals(); |
846 }); | 863 }); |
847 } | 864 } |
848 | 865 |
849 /** | 866 /** |
850 * Submits a task to send pending dismissals. | 867 * Submits a task to send pending dismissals. |
851 */ | 868 */ |
852 function retryPendingDismissals() { | 869 function retryPendingDismissals() { |
853 tasks.add(RETRY_DISMISS_TASK_NAME, function() { | 870 tasks.add(RETRY_DISMISS_TASK_NAME, function() { |
854 dismissalAttempts.planForNext(function() { | 871 processPendingDismissals().catch(dismissalAttempts.scheduleRetry); |
855 processPendingDismissals(); | |
856 }); | |
857 }); | 872 }); |
858 } | 873 } |
859 | 874 |
860 /** | 875 /** |
861 * Opens a URL in a new tab. | 876 * Opens a URL in a new tab. |
862 * @param {string} url URL to open. | 877 * @param {string} url URL to open. |
863 */ | 878 */ |
864 function openUrl(url) { | 879 function openUrl(url) { |
865 instrumented.tabs.create({url: url}, function(tab) { | 880 instrumented.tabs.create({url: url}, function(tab) { |
866 if (tab) | 881 if (tab) |
(...skipping 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
999 else | 1014 else |
1000 stopPollingCards(); | 1015 stopPollingCards(); |
1001 } else { | 1016 } else { |
1002 console.log( | 1017 console.log( |
1003 'Action Ignored setShouldPollCards=' + shouldPollCardsRequest); | 1018 'Action Ignored setShouldPollCards=' + shouldPollCardsRequest); |
1004 } | 1019 } |
1005 }); | 1020 }); |
1006 } | 1021 } |
1007 | 1022 |
1008 /** | 1023 /** |
| 1024 * Starts or stops the optin check. |
| 1025 * @param {boolean} shouldPollOptInStatus true to start and false to stop |
| 1026 * polling the optin status. |
| 1027 */ |
| 1028 function setShouldPollOptInStatus(shouldPollOptInStatus) { |
| 1029 optInPollAttempts.isRunning(function(currentValue) { |
| 1030 if (shouldPollOptInStatus != currentValue) { |
| 1031 console.log( |
| 1032 'Action Taken setShouldPollOptInStatus=' + shouldPollOptInStatus); |
| 1033 if (shouldPollOptInStatus) { |
| 1034 pollOptedInNoImmediateRecheck(); |
| 1035 } else { |
| 1036 optInPollAttempts.stop(); |
| 1037 } |
| 1038 } else { |
| 1039 console.log( |
| 1040 'Action Ignored setShouldPollOptInStatus=' + shouldPollOptInStatus); |
| 1041 } |
| 1042 }); |
| 1043 } |
| 1044 |
| 1045 /** |
1009 * Enables or disables the Google Now background permission. | 1046 * Enables or disables the Google Now background permission. |
1010 * @param {boolean} backgroundEnable true to run in the background. | 1047 * @param {boolean} backgroundEnable true to run in the background. |
1011 * false to not run in the background. | 1048 * false to not run in the background. |
1012 */ | 1049 */ |
1013 function setBackgroundEnable(backgroundEnable) { | 1050 function setBackgroundEnable(backgroundEnable) { |
1014 instrumented.permissions.contains({permissions: ['background']}, | 1051 instrumented.permissions.contains({permissions: ['background']}, |
1015 function(hasPermission) { | 1052 function(hasPermission) { |
1016 if (backgroundEnable != hasPermission) { | 1053 if (backgroundEnable != hasPermission) { |
1017 console.log('Action Taken setBackgroundEnable=' + backgroundEnable); | 1054 console.log('Action Taken setBackgroundEnable=' + backgroundEnable); |
1018 if (backgroundEnable) | 1055 if (backgroundEnable) |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1059 canEnableBackground, | 1096 canEnableBackground, |
1060 notificationEnabled, | 1097 notificationEnabled, |
1061 googleNowEnabled) { | 1098 googleNowEnabled) { |
1062 console.log( | 1099 console.log( |
1063 'State Update signedIn=' + signedIn + ' ' + | 1100 'State Update signedIn=' + signedIn + ' ' + |
1064 'canEnableBackground=' + canEnableBackground + ' ' + | 1101 'canEnableBackground=' + canEnableBackground + ' ' + |
1065 'notificationEnabled=' + notificationEnabled + ' ' + | 1102 'notificationEnabled=' + notificationEnabled + ' ' + |
1066 'googleNowEnabled=' + googleNowEnabled); | 1103 'googleNowEnabled=' + googleNowEnabled); |
1067 | 1104 |
1068 var shouldPollCards = false; | 1105 var shouldPollCards = false; |
| 1106 var shouldPollOptInStatus = false; |
1069 var shouldSetBackground = false; | 1107 var shouldSetBackground = false; |
1070 var shouldClearCards = true; | |
1071 | 1108 |
1072 if (signedIn && notificationEnabled) { | 1109 if (signedIn && notificationEnabled) { |
1073 shouldClearCards = !googleNowEnabled; | 1110 shouldPollCards = googleNowEnabled; |
| 1111 shouldPollOptInStatus = !googleNowEnabled; |
1074 shouldSetBackground = canEnableBackground && googleNowEnabled; | 1112 shouldSetBackground = canEnableBackground && googleNowEnabled; |
1075 shouldPollCards = true; | |
1076 } else { | 1113 } else { |
1077 recordEvent(GoogleNowEvent.STOPPED); | 1114 recordEvent(GoogleNowEvent.STOPPED); |
1078 } | 1115 } |
1079 | 1116 |
1080 recordEventIfNoCards(signedIn, notificationEnabled, googleNowEnabled); | 1117 recordEventIfNoCards(signedIn, notificationEnabled, googleNowEnabled); |
1081 | 1118 |
1082 console.log( | 1119 console.log( |
1083 'Requested Actions shouldSetBackground=' + shouldSetBackground + ' ' + | 1120 'Requested Actions shouldSetBackground=' + shouldSetBackground + ' ' + |
1084 'setShouldPollCards=' + shouldPollCards + ' ' + | 1121 'setShouldPollCards=' + shouldPollCards + ' ' + |
1085 'shouldClearCards=' + shouldClearCards); | 1122 'shouldPollOptInStatus=' + shouldPollOptInStatus); |
1086 | 1123 |
1087 setBackgroundEnable(shouldSetBackground); | 1124 setBackgroundEnable(shouldSetBackground); |
1088 setShouldPollCards(shouldPollCards); | 1125 setShouldPollCards(shouldPollCards); |
1089 if (shouldClearCards) { | 1126 setShouldPollOptInStatus(shouldPollOptInStatus); |
| 1127 if (!shouldPollCards) { |
1090 removeAllCards(); | 1128 removeAllCards(); |
1091 } | 1129 } |
1092 } | 1130 } |
1093 | 1131 |
1094 /** | 1132 /** |
1095 * Coordinates the behavior of Google Now for Chrome depending on | 1133 * Coordinates the behavior of Google Now for Chrome depending on |
1096 * Chrome and extension state. | 1134 * Chrome and extension state. |
1097 */ | 1135 */ |
1098 function onStateChange() { | 1136 function onStateChange() { |
1099 tasks.add(STATE_CHANGED_TASK_NAME, function() { | 1137 tasks.add(STATE_CHANGED_TASK_NAME, function() { |
(...skipping 45 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1145 .then(function(items) { | 1183 .then(function(items) { |
1146 return items.googleNowEnabled; | 1184 return items.googleNowEnabled; |
1147 }); | 1185 }); |
1148 } | 1186 } |
1149 | 1187 |
1150 /** | 1188 /** |
1151 * Polls the optin state. | 1189 * Polls the optin state. |
1152 * Sometimes we get the response to the opted in result too soon during | 1190 * Sometimes we get the response to the opted in result too soon during |
1153 * push messaging. We'll recheck the optin state a few times before giving up. | 1191 * push messaging. We'll recheck the optin state a few times before giving up. |
1154 */ | 1192 */ |
1155 function pollOptedIn() { | 1193 function pollOptedInWithRecheck() { |
1156 /** | 1194 /** |
1157 * Cleans up any state used to recheck the opt-in poll. | 1195 * Cleans up any state used to recheck the opt-in poll. |
1158 */ | 1196 */ |
1159 function clearPollingState() { | 1197 function clearPollingState() { |
1160 localStorage.removeItem('optedInCheckCount'); | 1198 localStorage.removeItem('optedInCheckCount'); |
1161 optInCheckAttempts.stop(); | 1199 optInRecheckAttempts.stop(); |
1162 } | 1200 } |
1163 | 1201 |
1164 if (localStorage.optedInCheckCount === undefined) { | 1202 if (localStorage.optedInCheckCount === undefined) { |
1165 localStorage.optedInCheckCount = 0; | 1203 localStorage.optedInCheckCount = 0; |
1166 optInCheckAttempts.start(); | 1204 optInRecheckAttempts.start(); |
1167 } | 1205 } |
1168 | 1206 |
1169 console.log(new Date() + | 1207 console.log(new Date() + |
1170 ' checkOptedIn Attempt ' + localStorage.optedInCheckCount); | 1208 ' checkOptedIn Attempt ' + localStorage.optedInCheckCount); |
1171 | 1209 |
1172 requestAndUpdateOptedIn().then(function() { | 1210 requestAndUpdateOptedIn().then(function(optedIn) { |
1173 clearPollingState(); | 1211 if (optedIn) { |
1174 requestCards(); | 1212 clearPollingState(); |
| 1213 return Promise.resolve(); |
| 1214 } else { |
| 1215 // If we're not opted in, reject to retry. |
| 1216 return Promise.reject(); |
| 1217 } |
1175 }).catch(function() { | 1218 }).catch(function() { |
1176 if (localStorage.optedInCheckCount < 5) { | 1219 if (localStorage.optedInCheckCount < 5) { |
1177 localStorage.optedInCheckCount++; | 1220 localStorage.optedInCheckCount++; |
1178 optInCheckAttempts.planForNext(function() {}); | 1221 optInRecheckAttempts.scheduleRetry(); |
1179 } else { | 1222 } else { |
1180 clearPollingState(); | 1223 clearPollingState(); |
1181 } | 1224 } |
1182 }); | 1225 }); |
1183 } | 1226 } |
1184 | 1227 |
1185 instrumented.runtime.onInstalled.addListener(function(details) { | 1228 instrumented.runtime.onInstalled.addListener(function(details) { |
1186 console.log('onInstalled ' + JSON.stringify(details)); | 1229 console.log('onInstalled ' + JSON.stringify(details)); |
1187 if (details.reason != 'chrome_update') { | 1230 if (details.reason != 'chrome_update') { |
1188 initialize(); | 1231 initialize(); |
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1252 instrumented.notifications.onPermissionLevelChanged.addListener( | 1295 instrumented.notifications.onPermissionLevelChanged.addListener( |
1253 function(permissionLevel) { | 1296 function(permissionLevel) { |
1254 console.log('Notifications permissionLevel Change'); | 1297 console.log('Notifications permissionLevel Change'); |
1255 onStateChange(); | 1298 onStateChange(); |
1256 }); | 1299 }); |
1257 | 1300 |
1258 instrumented.notifications.onShowSettings.addListener(function() { | 1301 instrumented.notifications.onShowSettings.addListener(function() { |
1259 openUrl(SETTINGS_URL); | 1302 openUrl(SETTINGS_URL); |
1260 }); | 1303 }); |
1261 | 1304 |
| 1305 // Handles state change notifications for the Google Now enabled bit. |
| 1306 instrumented.storage.onChanged.addListener(function(changes, areaName) { |
| 1307 if (areaName === 'local') { |
| 1308 if ('googleNowEnabled' in changes) { |
| 1309 onStateChange(); |
| 1310 } |
| 1311 } |
| 1312 }); |
| 1313 |
1262 instrumented.pushMessaging.onMessage.addListener(function(message) { | 1314 instrumented.pushMessaging.onMessage.addListener(function(message) { |
1263 // message.payload will be '' when the extension first starts. | 1315 // message.payload will be '' when the extension first starts. |
1264 // Each time after signing in, we'll get latest payload for all channels. | 1316 // Each time after signing in, we'll get latest payload for all channels. |
1265 // So, we need to poll the server only when the payload is non-empty and has | 1317 // So, we need to poll the server only when the payload is non-empty and has |
1266 // changed. | 1318 // changed. |
1267 console.log('pushMessaging.onMessage ' + JSON.stringify(message)); | 1319 console.log('pushMessaging.onMessage ' + JSON.stringify(message)); |
1268 if (message.payload.indexOf('REQUEST_CARDS') == 0) { | 1320 if (message.payload.indexOf('REQUEST_CARDS') == 0) { |
1269 tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() { | 1321 tasks.add(ON_PUSH_MESSAGE_START_TASK_NAME, function() { |
1270 // Accept promise rejection on failure since it's safer to do nothing, | 1322 // Accept promise rejection on failure since it's safer to do nothing, |
1271 // preventing polling the server when the payload really didn't change. | 1323 // preventing polling the server when the payload really didn't change. |
1272 fillFromChromeLocalStorage({ | 1324 fillFromChromeLocalStorage({ |
1273 lastPollNowPayloads: {}, | 1325 lastPollNowPayloads: {}, |
1274 /** @type {Object.<string, StoredNotificationGroup>} */ | 1326 /** @type {Object.<string, StoredNotificationGroup>} */ |
1275 notificationGroups: {} | 1327 notificationGroups: {} |
1276 }, PromiseRejection.ALLOW).then(function(items) { | 1328 }, PromiseRejection.ALLOW).then(function(items) { |
1277 if (items.lastPollNowPayloads[message.subchannelId] != | 1329 if (items.lastPollNowPayloads[message.subchannelId] != |
1278 message.payload) { | 1330 message.payload) { |
1279 items.lastPollNowPayloads[message.subchannelId] = message.payload; | 1331 items.lastPollNowPayloads[message.subchannelId] = message.payload; |
1280 | 1332 |
1281 items.notificationGroups['PUSH' + message.subchannelId] = { | 1333 items.notificationGroups['PUSH' + message.subchannelId] = { |
1282 cards: [], | 1334 cards: [], |
1283 nextPollTime: Date.now() | 1335 nextPollTime: Date.now() |
1284 }; | 1336 }; |
1285 | 1337 |
1286 chrome.storage.local.set({ | 1338 chrome.storage.local.set({ |
1287 lastPollNowPayloads: items.lastPollNowPayloads, | 1339 lastPollNowPayloads: items.lastPollNowPayloads, |
1288 notificationGroups: items.notificationGroups | 1340 notificationGroups: items.notificationGroups |
1289 }); | 1341 }); |
1290 | 1342 |
1291 pollOptedIn(); | 1343 pollOptedInWithRecheck(); |
1292 } | 1344 } |
1293 }); | 1345 }); |
1294 }); | 1346 }); |
1295 } | 1347 } |
1296 }); | 1348 }); |
OLD | NEW |