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 06540f5878083e04588cb9cc0e11f49f4daf729e..c3aa0d86579fc1fc4dff4f56cbca21513f2aefac 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); |
@@ -330,6 +332,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. |
@@ -384,6 +405,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 |
@@ -395,15 +423,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(); |
+ } |
+ }); |
+ }); |
+ } |
}); |
}); |
} |
@@ -566,21 +598,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) {}); |
} |
/** |
@@ -596,7 +624,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; |
} |
@@ -642,26 +670,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(); |
+ 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); |
+ }); |
+ }); |
+ }); |
}); |
} |
@@ -687,6 +832,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') { |
@@ -699,6 +853,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'); |