Chromium Code Reviews| Index: chrome/browser/resources/google_now/background.js |
| diff --git a/chrome/browser/resources/google_now/background.js b/chrome/browser/resources/google_now/background.js |
| index 534ff651b1b81b3c267085967a7630fc0b3692e2..c737168ef029501fb90b77df2b4d3191d096a4e5 100644 |
| --- a/chrome/browser/resources/google_now/background.js |
| +++ b/chrome/browser/resources/google_now/background.js |
| @@ -75,6 +75,7 @@ var DISMISS_RETENTION_TIME_MS = 20 * 60 * 1000; // 20 minutes |
| var UPDATE_CARDS_TASK_NAME = 'update-cards'; |
| var DISMISS_CARD_TASK_NAME = 'dismiss-card'; |
| var RETRY_DISMISS_TASK_NAME = 'retry-dismiss'; |
| +var STATE_CHANGED_TASK_NAME = 'state-changed'; |
| var LOCATION_WATCH_NAME = 'location-watch'; |
| @@ -87,12 +88,6 @@ var WELCOME_TOAST_NOTIFICATION_ID = 'enable-now-toast'; |
| var ToastButtonIndex = {YES: 0, NO: 1}; |
| /** |
| - * The action that the user performed on the welcome toast. |
| - * @enum {number} |
| - */ |
| -var ToastOptionResponse = {CHOSE_YES: 1, CHOSE_NO: 2}; |
| - |
| -/** |
| * Checks if a new task can't be scheduled when another task is already |
| * scheduled. |
| * @param {string} newTaskName Name of the new task. |
| @@ -119,6 +114,9 @@ function areTasksConflicting(newTaskName, scheduledTaskName) { |
| return false; |
| } |
| +var googleGeolocationAccessEnabledPref = |
| + chrome.preferencesPrivate.googleGeolocationAccessEnabled; |
| + |
| var tasks = buildTaskManager(areTasksConflicting); |
| // Add error processing to API calls. |
| @@ -132,6 +130,10 @@ tasks.instrumentApiFunction( |
| chrome.notifications.onButtonClicked, 'addListener', 0); |
| tasks.instrumentApiFunction(chrome.notifications.onClicked, 'addListener', 0); |
| tasks.instrumentApiFunction(chrome.notifications.onClosed, 'addListener', 0); |
| +tasks.instrumentApiFunction( |
| + googleGeolocationAccessEnabledPref.onChange, |
| + 'addListener', |
| + 0); |
| tasks.instrumentApiFunction(chrome.runtime.onInstalled, 'addListener', 0); |
| tasks.instrumentApiFunction(chrome.runtime.onStartup, 'addListener', 0); |
| tasks.instrumentApiFunction(chrome.tabs, 'create', 1); |
| @@ -392,6 +394,25 @@ function parseAndShowNotificationCards(response, callback) { |
| } |
| /** |
| + * Removes all cards and card state on Google Now close down. |
| + * For example, this occurs when the geolocation preference is unchecked in the |
| + * content settings. |
| + */ |
| +function removeAllCards() { |
| + console.log('removeAllCards'); |
| + |
| + // TODO(robliao): Once Google Now clears its own checkbox in the |
| + // notifications center and bug 260376 is fixed, the below clearing |
| + // code is no longer necessary. |
| + chrome.notifications.getAll(function(notifications) { |
| + for (var notificationId in notifications) { |
| + chrome.notifications.clear(notificationId, function() {}); |
| + } |
| + storage.set({notificationsData: {}}); |
| + }); |
| +} |
| + |
| +/** |
| * Requests notification cards from the server. |
| * @param {Location} position Location of this computer. |
| * @param {function()} callback Completion callback. |
| @@ -446,6 +467,13 @@ function requestLocation() { |
| chrome.location.watchLocation(LOCATION_WATCH_NAME, {}); |
| } |
| +/** |
| + * Stops getting the location. |
| + */ |
| +function stopRequestLocation() { |
| + console.log('stopRequestLocation'); |
| + chrome.location.clearWatch(LOCATION_WATCH_NAME); |
| +} |
| /** |
| * Obtains new location; requests and shows notification cards based on this |
| @@ -457,15 +485,19 @@ function updateNotificationsCards(position) { |
| ' @' + new Date()); |
| tasks.add(UPDATE_CARDS_TASK_NAME, function(callback) { |
| console.log('updateNotificationsCards-task-begin'); |
| - updateCardsAttempts.planForNext(function() { |
| - processPendingDismissals(function(success) { |
| - if (success) { |
| - // The cards are requested only if there are no unsent dismissals. |
| - requestNotificationCards(position, callback); |
| - } else { |
| - callback(); |
| - } |
| - }); |
| + updateCardsAttempts.isRunning(function(running) { |
| + if (running) { |
| + updateCardsAttempts.planForNext(function() { |
| + processPendingDismissals(function(success) { |
| + if (success) { |
| + // The cards are requested only if there are no unsent dismissals. |
| + requestNotificationCards(position, callback); |
| + } else { |
| + callback(); |
| + } |
| + }); |
| + }); |
| + } |
| }); |
| }); |
| } |
| @@ -628,21 +660,17 @@ function onNotificationClicked(notificationId, selector) { |
| * @param {number} buttonIndex The index of the button which was clicked. |
| */ |
| function onToastNotificationClicked(buttonIndex) { |
| + storage.set({userRespondedToToast: true}); |
| + |
| if (buttonIndex == ToastButtonIndex.YES) { |
| chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastClickedYes'); |
| - storage.set({toastState: ToastOptionResponse.CHOSE_YES}); |
| - |
| - // TODO(zturner): Update chrome geolocation setting once the settings |
| - // API is in place. |
| - startPollingCards(); |
| + googleGeolocationAccessEnabledPref.set({value: true}); |
| + // The googlegeolocationaccessenabled preference change callback |
| + // will take care of starting the poll for cards. |
| } else { |
| chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastClickedNo'); |
| - storage.set({toastState: ToastOptionResponse.CHOSE_NO}); |
| + onStateChange(); |
| } |
| - |
| - chrome.notifications.clear( |
| - WELCOME_TOAST_NOTIFICATION_ID, |
| - function(wasCleared) {}); |
| } |
| /** |
| @@ -658,7 +686,7 @@ function onNotificationClosed(notificationId, byUser) { |
| // Even though they only closed the notification without clicking no, treat |
| // it as though they clicked No anwyay, and don't show the toast again. |
| chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastDismissed'); |
| - storage.set({toastState: ToastOptionResponse.CHOSE_NO}); |
| + storage.set({userRespondedToToast: true}); |
| return; |
| } |
| @@ -706,26 +734,143 @@ function startPollingCards() { |
| } |
| /** |
| + * Stops all machinery in the polling system. |
| + */ |
| +function stopPollingCards() { |
| + stopRequestLocation(); |
| + |
| + updateCardsAttempts.stop(); |
| + |
| + removeAllCards(); |
| +} |
| + |
| +/** |
| * Initializes the event page on install or on browser startup. |
| */ |
| function initialize() { |
| recordEvent(DiagnosticEvent.EXTENSION_START); |
| - storage.get('toastState', function(items) { |
| - // The toast state might be undefined (e.g. not in storage yet) if this is |
| - // the first time ever being prompted. |
| - |
| - // TODO(zturner): Get the value of isGeolocationEnabled from the settings |
| - // api and additionally make sure it is true. |
| - if (!items.toastState) { |
| - if (NOTIFICATION_CARDS_URL) { |
| - chrome.identity.getAuthToken({interactive: false}, function(token) { |
| - if (!chrome.runtime.lastError && token) |
| - showWelcomeToast(); |
| - }); |
| + |
| + // Alarms persist across chrome restarts. This is undesirable since it |
| + // prevents us from starting up everything (alarms are a heuristic to |
| + // determine if we are already running). To mitigate this, we will |
| + // shut everything down on initialize before starting everything up. |
| + stopPollingCards(); |
|
vadimt
2013/07/25 20:48:01
I'm afraid that this scenario is possible:
1. When
robliao
2013/07/25 22:35:48
It is, but that bug is beyond the scope of this de
vadimt
2013/07/26 01:00:37
Please add a TODO item and plan addressing this so
robliao
2013/07/26 01:12:17
This would be better expressed as a bug rather tha
|
| + onStateChange(); |
| +} |
| + |
| +/** |
| + * Starts or stops the polling of cards. |
| + * @param {boolean} shouldPollCardsRequest true to start and |
| + * false to stop polling cards. |
| + * @param {function} onSuccess Called on completion. |
| + */ |
| +function setShouldPollCards(shouldPollCardsRequest, onSuccess) { |
| + tasks.debugSetStepName( |
| + 'setShouldRun-shouldRun-updateCardsAttemptsIsRunning'); |
| + updateCardsAttempts.isRunning(function(currentValue) { |
| + if (shouldPollCardsRequest != currentValue) { |
| + if (shouldPollCardsRequest) |
| + startPollingCards(); |
| + else |
| + stopPollingCards(); |
| + } |
| + onSuccess(); |
| + }); |
| +} |
| + |
| +/** |
| + * Shows or hides the toast. |
| + * @param {boolean} visibleRequest true to show the toast and |
| + * false to hide the toast. |
| + * @param {function} onSuccess Called on completion. |
| + */ |
| +function setToastVisible(visibleRequest, onSuccess) { |
| + tasks.debugSetStepName( |
| + 'setToastVisible-shouldSetToastVisible-getAllNotifications'); |
| + chrome.notifications.getAll(function(notifications) { |
| + // TODO(vadimt): Figure out what to do when notifications are disabled for |
| + // our extension. |
| + notifications = notifications || {}; |
| + |
| + if (visibleRequest != !!notifications[WELCOME_TOAST_NOTIFICATION_ID]) { |
| + if (visibleRequest) |
| + showWelcomeToast(); |
| + else |
| + hideWelcomeToast(); |
| + } |
| + |
| + onSuccess(); |
| + }); |
| +} |
| + |
| +/** |
| + * Does the actual work of deciding what Google Now should do |
| + * based off of the current state of Chrome. |
| + * @param {boolean} signedIn true if the user is signed in. |
| + * @param {boolean} geolocationEnabled true if |
| + * the geolocation option is enabled. |
| + * @param {boolean} userRespondedToToast true if |
| + * the user has responded to the toast. |
| + * @param {function()} callback Call this function on completion. |
| + */ |
| +function updateRunningState( |
| + signedIn, |
| + geolocationEnabled, |
| + userRespondedToToast, |
| + callback) { |
| + |
| + var shouldSetToastVisible = false; |
| + var shouldPollCards = false; |
| + |
| + if (signedIn) { |
| + if (geolocationEnabled) { |
| + if (!userRespondedToToast) { |
| + // If the user enabled geolocation independently of Google Now, |
| + // the user has implicitly responded to the toast. |
| + // We do not want to show it again. |
| + storage.set({userRespondedToToast: true}); |
| } |
| - } else if (items.toastState == ToastOptionResponse.CHOSE_YES) { |
| - startPollingCards(); |
| + |
| + shouldPollCards = true; |
| + } else { |
| + if (!userRespondedToToast) |
| + shouldSetToastVisible = true; |
| } |
| + } |
| + |
| + setToastVisible(shouldSetToastVisible, function() { |
| + setShouldPollCards(shouldPollCards, callback); |
| + }); |
| +} |
| + |
| +/** |
| + * Coordinates the behavior of Google Now for Chrome depending on |
| + * Chrome and extension state. |
| + */ |
| +function onStateChange() { |
| + tasks.add(STATE_CHANGED_TASK_NAME, function(callback) { |
| + tasks.debugSetStepName('onStateChange-getAuthToken'); |
| + chrome.identity.getAuthToken({interactive: false}, function(token) { |
| + var signedIn = |
| + !chrome.runtime.lastError && |
| + token && |
| + !!NOTIFICATION_CARDS_URL; |
| + tasks.debugSetStepName( |
| + 'onStateChange-get-googleGeolocationAccessEnabledPref'); |
| + googleGeolocationAccessEnabledPref.get({}, function(prefValue) { |
| + var geolocationEnabled = !!prefValue.value; |
| + tasks.debugSetStepName( |
| + 'onStateChange-get-userRespondedToToast'); |
| + storage.get('userRespondedToToast', function(items) { |
| + var userRespondedToToast = !!items.userRespondedToToast; |
| + updateRunningState( |
| + signedIn, |
| + geolocationEnabled, |
| + userRespondedToToast, |
| + callback); |
| + }); |
| + }); |
| + }); |
| }); |
| } |
| @@ -751,6 +896,15 @@ function showWelcomeToast() { |
| function(notificationId) {}); |
| } |
| +/** |
| + * Hides the welcome toast. |
| + */ |
| +function hideWelcomeToast() { |
| + chrome.notifications.clear( |
| + WELCOME_TOAST_NOTIFICATION_ID, |
| + function() {}); |
| +} |
| + |
| chrome.runtime.onInstalled.addListener(function(details) { |
| console.log('onInstalled ' + JSON.stringify(details)); |
| if (details.reason != 'chrome_update') { |
| @@ -763,6 +917,11 @@ chrome.runtime.onStartup.addListener(function() { |
| initialize(); |
| }); |
| +googleGeolocationAccessEnabledPref.onChange.addListener(function(prefValue) { |
| + console.log('googleGeolocationAccessEnabledPref onChange ' + prefValue.value); |
| + onStateChange(); |
| +}); |
| + |
| chrome.notifications.onClicked.addListener( |
| function(notificationId) { |
| chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked'); |