| 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 |