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 12 matching lines...) Expand all Loading... | |
23 // TODO(vadimt): Honor the flag the enables Google Now integration. | 23 // TODO(vadimt): Honor the flag the enables Google Now integration. |
24 // TODO(vadimt): Figure out the final values of the constants. | 24 // TODO(vadimt): Figure out the final values of the constants. |
25 // TODO(vadimt): Remove 'console' calls. | 25 // TODO(vadimt): Remove 'console' calls. |
26 // TODO(vadimt): Consider sending JS stacks for malformed server responses. | 26 // TODO(vadimt): Consider sending JS stacks for malformed server responses. |
27 | 27 |
28 /** | 28 /** |
29 * Standard response code for successful HTTP requests. This is the only success | 29 * Standard response code for successful HTTP requests. This is the only success |
30 * code the server will send. | 30 * code the server will send. |
31 */ | 31 */ |
32 var HTTP_OK = 200; | 32 var HTTP_OK = 200; |
33 var HTTP_NOCONTENT = 204; | |
33 | 34 |
34 var HTTP_BAD_REQUEST = 400; | 35 var HTTP_BAD_REQUEST = 400; |
35 var HTTP_UNAUTHORIZED = 401; | 36 var HTTP_UNAUTHORIZED = 401; |
36 var HTTP_FORBIDDEN = 403; | 37 var HTTP_FORBIDDEN = 403; |
37 var HTTP_METHOD_NOT_ALLOWED = 405; | 38 var HTTP_METHOD_NOT_ALLOWED = 405; |
38 | 39 |
40 var MS_IN_SECOND = 1000; | |
41 var MS_IN_MINUTE = 60 * 1000; | |
42 | |
39 /** | 43 /** |
40 * Initial period for polling for Google Now Notifications cards to use when the | 44 * Initial period for polling for Google Now Notifications cards to use when the |
41 * period from the server is not available. | 45 * period from the server is not available. |
42 */ | 46 */ |
43 var INITIAL_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes | 47 var INITIAL_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes |
44 | 48 |
45 /** | 49 /** |
50 * Mininal period for polling for Google Now Notifications cards. | |
51 */ | |
52 var MINIMUM_POLLING_PERIOD_SECONDS = 5 * 60; // 5 minutes | |
53 | |
54 /** | |
46 * Maximal period for polling for Google Now Notifications cards to use when the | 55 * Maximal period for polling for Google Now Notifications cards to use when the |
47 * period from the server is not available. | 56 * period from the server is not available. |
48 */ | 57 */ |
49 var MAXIMUM_POLLING_PERIOD_SECONDS = 60 * 60; // 1 hour | 58 var MAXIMUM_POLLING_PERIOD_SECONDS = 60 * 60; // 1 hour |
50 | 59 |
51 /** | 60 /** |
52 * Initial period for retrying the server request for dismissing cards. | 61 * Initial period for retrying the server request for dismissing cards. |
53 */ | 62 */ |
54 var INITIAL_RETRY_DISMISS_PERIOD_SECONDS = 60; // 1 minute | 63 var INITIAL_RETRY_DISMISS_PERIOD_SECONDS = 60; // 1 minute |
55 | 64 |
(...skipping 24 matching lines...) Expand all Loading... | |
80 | 89 |
81 var WELCOME_TOAST_NOTIFICATION_ID = 'enable-now-toast'; | 90 var WELCOME_TOAST_NOTIFICATION_ID = 'enable-now-toast'; |
82 | 91 |
83 /** | 92 /** |
84 * The indices of the buttons that are displayed on the welcome toast. | 93 * The indices of the buttons that are displayed on the welcome toast. |
85 * @enum {number} | 94 * @enum {number} |
86 */ | 95 */ |
87 var ToastButtonIndex = {YES: 0, NO: 1}; | 96 var ToastButtonIndex = {YES: 0, NO: 1}; |
88 | 97 |
89 /** | 98 /** |
99 * Notification as it's sent by the server. | |
100 * | |
101 * @typedef {{ | |
102 * notificationId: string, | |
103 * chromeNotificationId: string, | |
104 * trigger: Object=, | |
105 * version: number, | |
106 * chromeNotificationOptions: Object, | |
107 * actionUrls: Object=, | |
108 * dismissal: Object | |
robliao
2013/10/01 20:08:04
This is also marked as optional.
vadimt
2013/10/01 20:11:51
Will be unmarked as such on the server (see the th
| |
109 * }} | |
110 */ | |
111 var UnmergedNotification; | |
112 | |
113 /** | |
114 * Notification group as the client stores it. |cardsTimestamp| and |rank| are | |
115 * defined if |cards| is non-empty. | |
116 * | |
117 * @typedef {{ | |
118 * cards: Array.<UnmergedNotification>, | |
119 * cardsTimestamp: number=, | |
120 * nextPollTime: number, | |
121 * rank: number= | |
122 * }} | |
123 */ | |
124 var StorageGroup; | |
125 | |
126 /** | |
90 * Checks if a new task can't be scheduled when another task is already | 127 * Checks if a new task can't be scheduled when another task is already |
91 * scheduled. | 128 * scheduled. |
92 * @param {string} newTaskName Name of the new task. | 129 * @param {string} newTaskName Name of the new task. |
93 * @param {string} scheduledTaskName Name of the scheduled task. | 130 * @param {string} scheduledTaskName Name of the scheduled task. |
94 * @return {boolean} Whether the new task conflicts with the existing task. | 131 * @return {boolean} Whether the new task conflicts with the existing task. |
95 */ | 132 */ |
96 function areTasksConflicting(newTaskName, scheduledTaskName) { | 133 function areTasksConflicting(newTaskName, scheduledTaskName) { |
97 if (newTaskName == UPDATE_CARDS_TASK_NAME && | 134 if (newTaskName == UPDATE_CARDS_TASK_NAME && |
98 scheduledTaskName == UPDATE_CARDS_TASK_NAME) { | 135 scheduledTaskName == UPDATE_CARDS_TASK_NAME) { |
99 // If a card update is requested while an old update is still scheduled, we | 136 // If a card update is requested while an old update is still scheduled, we |
(...skipping 11 matching lines...) Expand all Loading... | |
111 } | 148 } |
112 | 149 |
113 return false; | 150 return false; |
114 } | 151 } |
115 | 152 |
116 var tasks = buildTaskManager(areTasksConflicting); | 153 var tasks = buildTaskManager(areTasksConflicting); |
117 | 154 |
118 // Add error processing to API calls. | 155 // Add error processing to API calls. |
119 wrapper.instrumentChromeApiFunction('location.onLocationUpdate.addListener', 0); | 156 wrapper.instrumentChromeApiFunction('location.onLocationUpdate.addListener', 0); |
120 wrapper.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1); | 157 wrapper.instrumentChromeApiFunction('metricsPrivate.getVariationParams', 1); |
158 wrapper.instrumentChromeApiFunction('notifications.clear', 1); | |
121 wrapper.instrumentChromeApiFunction('notifications.create', 2); | 159 wrapper.instrumentChromeApiFunction('notifications.create', 2); |
122 wrapper.instrumentChromeApiFunction('notifications.update', 2); | 160 wrapper.instrumentChromeApiFunction('notifications.update', 2); |
123 wrapper.instrumentChromeApiFunction('notifications.getAll', 0); | 161 wrapper.instrumentChromeApiFunction('notifications.getAll', 0); |
124 wrapper.instrumentChromeApiFunction( | 162 wrapper.instrumentChromeApiFunction( |
125 'notifications.onButtonClicked.addListener', 0); | 163 'notifications.onButtonClicked.addListener', 0); |
126 wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0); | 164 wrapper.instrumentChromeApiFunction('notifications.onClicked.addListener', 0); |
127 wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0); | 165 wrapper.instrumentChromeApiFunction('notifications.onClosed.addListener', 0); |
128 wrapper.instrumentChromeApiFunction('omnibox.onInputEntered.addListener', 0); | 166 wrapper.instrumentChromeApiFunction('omnibox.onInputEntered.addListener', 0); |
129 wrapper.instrumentChromeApiFunction( | 167 wrapper.instrumentChromeApiFunction( |
130 'preferencesPrivate.googleGeolocationAccessEnabled.get', | 168 'preferencesPrivate.googleGeolocationAccessEnabled.get', |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
190 chrome.metricsPrivate.recordValue(metricDescription, event); | 228 chrome.metricsPrivate.recordValue(metricDescription, event); |
191 } | 229 } |
192 | 230 |
193 /** | 231 /** |
194 * Adds authorization behavior to the request. | 232 * Adds authorization behavior to the request. |
195 * @param {XMLHttpRequest} request Server request. | 233 * @param {XMLHttpRequest} request Server request. |
196 * @param {function(boolean)} callbackBoolean Completion callback with 'success' | 234 * @param {function(boolean)} callbackBoolean Completion callback with 'success' |
197 * parameter. | 235 * parameter. |
198 */ | 236 */ |
199 function setAuthorization(request, callbackBoolean) { | 237 function setAuthorization(request, callbackBoolean) { |
200 tasks.debugSetStepName('setAuthorization-isSignedIn'); | |
201 authenticationManager.isSignedIn(function(token) { | 238 authenticationManager.isSignedIn(function(token) { |
202 if (!token) { | 239 if (!token) { |
203 callbackBoolean(false); | 240 callbackBoolean(false); |
204 return; | 241 return; |
205 } | 242 } |
206 | 243 |
207 request.setRequestHeader('Authorization', 'Bearer ' + token); | 244 request.setRequestHeader('Authorization', 'Bearer ' + token); |
208 | 245 |
209 // Instrument onloadend to remove stale auth tokens. | 246 // Instrument onloadend to remove stale auth tokens. |
210 var originalOnLoadEnd = request.onloadend; | 247 var originalOnLoadEnd = request.onloadend; |
211 request.onloadend = wrapper.wrapCallback(function(event) { | 248 request.onloadend = wrapper.wrapCallback(function(event) { |
212 if (request.status == HTTP_FORBIDDEN || | 249 if (request.status == HTTP_FORBIDDEN || |
213 request.status == HTTP_UNAUTHORIZED) { | 250 request.status == HTTP_UNAUTHORIZED) { |
214 tasks.debugSetStepName('setAuthorization-removeToken'); | |
215 authenticationManager.removeToken(token, function() { | 251 authenticationManager.removeToken(token, function() { |
216 originalOnLoadEnd(event); | 252 originalOnLoadEnd(event); |
217 }); | 253 }); |
218 } else { | 254 } else { |
219 originalOnLoadEnd(event); | 255 originalOnLoadEnd(event); |
220 } | 256 } |
221 }); | 257 }); |
222 | 258 |
223 callbackBoolean(true); | 259 callbackBoolean(true); |
224 }); | 260 }); |
225 } | 261 } |
226 | 262 |
227 /** | 263 /** |
228 * Parses JSON response from the notification server, show notifications and | 264 * Shows parsed and merged cards as notifications. |
229 * schedule next update. | 265 * @param {Object.<string, MergedCard>} cards Map from chromeNotificationId to |
230 * @param {string} response Server response. | 266 * the merged card, containing cards to show. |
231 * @param {function()} callback Completion callback. | |
232 */ | 267 */ |
233 function parseAndShowNotificationCards(response, callback) { | 268 function showNotificationCards(cards) { |
234 console.log('parseAndShowNotificationCards ' + response); | 269 console.log('showNotificationCards ' + JSON.stringify(cards)); |
235 try { | |
236 var parsedResponse = JSON.parse(response); | |
237 } catch (error) { | |
238 console.error('parseAndShowNotificationCards parse error: ' + error); | |
239 callback(); | |
240 return; | |
241 } | |
242 | 270 |
243 var cards = parsedResponse.cards; | |
244 | |
245 if (!(cards instanceof Array)) { | |
246 callback(); | |
247 return; | |
248 } | |
249 | |
250 if (typeof parsedResponse.next_poll_seconds != 'number') { | |
251 callback(); | |
252 return; | |
253 } | |
254 | |
255 tasks.debugSetStepName('parseAndShowNotificationCards-storage-get'); | |
256 instrumented.storage.local.get(['notificationsData', 'recentDismissals'], | 271 instrumented.storage.local.get(['notificationsData', 'recentDismissals'], |
257 function(items) { | 272 function(items) { |
258 console.log('parseAndShowNotificationCards-get ' + | 273 console.log('parseAndShowNotificationCards-get ' + |
rgustafson
2013/10/04 16:56:47
showNotificationCards-get
vadimt
2013/10/07 20:29:54
Done.
| |
259 JSON.stringify(items)); | 274 JSON.stringify(items)); |
260 items.notificationsData = items.notificationsData || {}; | 275 items.notificationsData = items.notificationsData || {}; |
261 items.recentDismissals = items.recentDismissals || {}; | 276 items.recentDismissals = items.recentDismissals || {}; |
262 | 277 |
263 tasks.debugSetStepName( | |
264 'parseAndShowNotificationCards-notifications-getAll'); | |
265 instrumented.notifications.getAll(function(notifications) { | 278 instrumented.notifications.getAll(function(notifications) { |
266 console.log('parseAndShowNotificationCards-getAll ' + | 279 console.log('parseAndShowNotificationCards-getAll ' + |
rgustafson
2013/10/04 16:56:47
showNotificationCards-getAll
vadimt
2013/10/07 20:29:54
Done.
| |
267 JSON.stringify(notifications)); | 280 JSON.stringify(notifications)); |
268 // TODO(vadimt): Figure out what to do when notifications are | 281 // TODO(vadimt): Figure out what to do when notifications are |
269 // disabled for our extension. | 282 // disabled for our extension. |
270 notifications = notifications || {}; | 283 notifications = notifications || {}; |
271 | 284 |
272 // Build a set of non-expired recent dismissals. It will be used for | 285 // Build a set of non-expired recent dismissals. It will be used for |
273 // client-side filtering of cards. | 286 // client-side filtering of cards. |
274 var updatedRecentDismissals = {}; | 287 var updatedRecentDismissals = {}; |
275 var currentTimeMs = Date.now(); | 288 var currentTimeMs = Date.now(); |
276 for (var notificationId in items.recentDismissals) { | 289 for (var chromeNotificationId in items.recentDismissals) { |
277 if (currentTimeMs - items.recentDismissals[notificationId] < | 290 if (currentTimeMs - items.recentDismissals[chromeNotificationId] < |
278 DISMISS_RETENTION_TIME_MS) { | 291 DISMISS_RETENTION_TIME_MS) { |
279 updatedRecentDismissals[notificationId] = | 292 updatedRecentDismissals[chromeNotificationId] = |
280 items.recentDismissals[notificationId]; | 293 items.recentDismissals[chromeNotificationId]; |
281 } | 294 delete cards[chromeNotificationId]; |
282 } | |
283 | |
284 // Mark existing notifications that received an update in this server | |
285 // response. | |
286 var updatedNotifications = {}; | |
287 | |
288 for (var i = 0; i < cards.length; ++i) { | |
289 var notificationId = cards[i].notificationId; | |
290 if (!(notificationId in updatedRecentDismissals) && | |
291 notificationId in notifications) { | |
292 updatedNotifications[notificationId] = true; | |
293 } | 295 } |
294 } | 296 } |
295 | 297 |
296 // Delete notifications that didn't receive an update. | 298 // Delete notifications that didn't receive an update. |
297 for (var notificationId in notifications) { | 299 for (var chromeNotificationId in notifications) { |
298 console.log('parseAndShowNotificationCards-delete-check ' + | 300 console.log('parseAndShowNotificationCards-delete-check ' + |
299 notificationId); | 301 chromeNotificationId); |
300 if (!(notificationId in updatedNotifications)) { | 302 if (!(chromeNotificationId in cards)) { |
301 console.log('parseAndShowNotificationCards-delete ' + | 303 console.log( |
302 notificationId); | 304 'showNotificationCards-delete ' + chromeNotificationId); |
303 cardSet.clear(notificationId); | 305 cardSet.clear(chromeNotificationId); |
304 } | 306 } |
305 } | 307 } |
306 | 308 |
307 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); | |
308 | |
309 // Create/update notifications and store their new properties. | 309 // Create/update notifications and store their new properties. |
310 var newNotificationsData = {}; | 310 var newNotificationsData = {}; |
311 for (var i = 0; i < cards.length; ++i) { | 311 for (var chromeNotificationId in cards) { |
312 var card = cards[i]; | 312 var notificationData = |
313 if (!(card.notificationId in updatedRecentDismissals)) { | 313 items.notificationsData[chromeNotificationId]; |
314 var notificationData = | 314 var previousVersion = notifications[chromeNotificationId] && |
315 items.notificationsData[card.notificationId]; | 315 notificationData && |
316 var previousVersion = notifications[card.notificationId] && | 316 notificationData.cardCreateInfo && |
317 notificationData && | 317 notificationData.cardCreateInfo.version; |
318 notificationData.cardCreateInfo && | 318 newNotificationsData[chromeNotificationId] = cardSet.update( |
319 notificationData.cardCreateInfo.version; | 319 chromeNotificationId, |
320 newNotificationsData[card.notificationId] = | 320 cards[chromeNotificationId], |
321 cardSet.update(card, previousVersion); | 321 previousVersion); |
322 } | |
323 } | 322 } |
324 | 323 |
325 updateCardsAttempts.start(parsedResponse.next_poll_seconds); | 324 recordEvent(GoogleNowEvent.CARDS_PARSE_SUCCESS); |
326 | 325 |
327 chrome.storage.local.set({ | 326 chrome.storage.local.set({ |
328 notificationsData: newNotificationsData, | 327 notificationsData: newNotificationsData, |
329 recentDismissals: updatedRecentDismissals | 328 recentDismissals: updatedRecentDismissals |
330 }); | 329 }); |
331 callback(); | |
332 }); | 330 }); |
333 }); | 331 }); |
334 } | 332 } |
335 | 333 |
336 /** | 334 /** |
337 * Removes all cards and card state on Google Now close down. | 335 * Removes all cards and card state on Google Now close down. |
338 * For example, this occurs when the geolocation preference is unchecked in the | 336 * For example, this occurs when the geolocation preference is unchecked in the |
339 * content settings. | 337 * content settings. |
340 */ | 338 */ |
341 function removeAllCards() { | 339 function removeAllCards() { |
342 console.log('removeAllCards'); | 340 console.log('removeAllCards'); |
343 | 341 |
344 // TODO(robliao): Once Google Now clears its own checkbox in the | 342 // TODO(robliao): Once Google Now clears its own checkbox in the |
345 // notifications center and bug 260376 is fixed, the below clearing | 343 // notifications center and bug 260376 is fixed, the below clearing |
346 // code is no longer necessary. | 344 // code is no longer necessary. |
347 instrumented.notifications.getAll(function(notifications) { | 345 instrumented.notifications.getAll(function(notifications) { |
348 notifications = notifications || {}; | 346 notifications = notifications || {}; |
349 for (var notificationId in notifications) { | 347 for (var chromeNotificationId in notifications) { |
350 chrome.notifications.clear(notificationId, function() {}); | 348 instrumented.notifications.clear(chromeNotificationId, function() {}); |
351 } | 349 } |
352 chrome.storage.local.set({notificationsData: {}}); | 350 chrome.storage.local.remove(['notificationsData', 'notificationGroups']); |
351 }); | |
352 } | |
353 | |
354 /** | |
355 * Merges an unmerged notification into a merged card with same ID. | |
rgustafson
2013/10/04 16:56:47
Change to "an existing merged card" to make it cle
vadimt
2013/10/07 20:29:54
The merged card may not exist at this moment, in w
| |
356 * @param {MergedCard=} mergedCard Existing merged card or undefined if a merged | |
357 * card doesn't exist (i.e. we see this ID for the first time while | |
358 * merging). | |
359 * @param {UnmergedNotification} unmergedNotification Notification as it was | |
360 * received from the server. | |
361 * @param {number} cardTimestamp The moment the unmerged card was received. | |
rgustafson
2013/10/04 16:56:47
You're using notification as the terminology for d
vadimt
2013/10/07 20:29:54
Done.
| |
362 * @param {number} cardGroupRank Rank of the group of the unmerged card. | |
rgustafson
2013/10/04 16:56:47
Same as above. notificationGroupRank
vadimt
2013/10/07 20:29:54
Done.
| |
363 * @return {MergedCard} Result of merging |unmergedNotification| into | |
364 * |mergedCard|. | |
365 */ | |
366 function mergeCards( | |
367 mergedCard, unmergedNotification, cardTimestamp, cardGroupRank) { | |
368 var result = mergedCard || {dismissals: []}; | |
369 | |
370 var priority = mergedCard ? | |
371 Math.max( | |
372 mergedCard.notification.priority, | |
373 unmergedNotification.chromeNotificationOptions.priority) : | |
374 unmergedNotification.chromeNotificationOptions.priority; | |
375 | |
376 if (!mergedCard || cardGroupRank > mergedCard.groupRank) { | |
377 result.groupRank = cardGroupRank; | |
378 var showTime = unmergedNotification.trigger && | |
379 unmergedNotification.trigger.showTimeSec && | |
380 cardTimestamp + unmergedNotification.trigger.showTimeSec * MS_IN_SECOND; | |
381 var hideTime = unmergedNotification.trigger && | |
382 unmergedNotification.trigger.hideTimeSec && | |
383 cardTimestamp + unmergedNotification.trigger.hideTimeSec * MS_IN_SECOND; | |
384 result.trigger = { | |
385 showTime: showTime, | |
386 hideTime: hideTime | |
387 }; | |
388 } | |
389 | |
390 if (!mergedCard || cardTimestamp > mergedCard.timestamp) { | |
rgustafson
2013/10/04 16:56:47
When will this not be true?
vadimt
2013/10/07 20:29:54
Your question assumes that we keep a collection of
| |
391 result.timestamp = cardTimestamp; | |
392 result.notification = unmergedNotification.chromeNotificationOptions; | |
393 result.actionUrls = unmergedNotification.actionUrls; | |
394 result.version = unmergedNotification.version; | |
395 } | |
396 | |
397 result.notification.priority = priority; | |
398 var dismissalData = { | |
399 notificationId: unmergedNotification.notificationId, | |
400 parameters: unmergedNotification.dismissal | |
401 }; | |
402 result.dismissals.push(dismissalData); | |
403 | |
404 return result; | |
405 } | |
406 | |
407 /** | |
408 * Merges a card group into a set of merged cards. | |
409 * @param {Object.<string, MergedCard>} mergedCards Set of merged cards. | |
rgustafson
2013/10/04 16:56:47
Map ... keyed by id?
vadimt
2013/10/07 20:29:54
Done.
| |
410 * This is an input/output parameter. | |
411 * @param {StorageGroup} storageGroup Group to merge into the merged card set. | |
412 */ | |
413 function mergeGroup(mergedCards, storageGroup) { | |
414 for (var i = 0; i < storageGroup.cards.length; i++) { | |
415 var card = storageGroup.cards[i]; | |
416 mergedCards[card.chromeNotificationId] = mergeCards( | |
417 mergedCards[card.chromeNotificationId], | |
418 card, | |
419 storageGroup.cardsTimestamp, | |
420 storageGroup.rank); | |
421 } | |
422 } | |
423 | |
424 /** | |
425 * Schedules next cards poll. | |
426 * @param {Object.<string, StorageGroup>} groups Map from group name to group | |
427 * information. | |
428 */ | |
429 function scheduleNextPoll(groups) { | |
430 var nextPollTime = null; | |
431 | |
432 for (var groupName in groups) { | |
433 var group = groups[groupName]; | |
434 nextPollTime = nextPollTime == null ? | |
435 group.nextPollTime : Math.min(group.nextPollTime, nextPollTime); | |
436 } | |
437 | |
438 verify(nextPollTime != null, 'scheduleNextPoll: nextPollTime is null'); | |
439 | |
440 var nextPollDelaySeconds = Math.max( | |
441 (nextPollTime - Date.now()) / MS_IN_SECOND, | |
442 MINIMUM_POLLING_PERIOD_SECONDS); | |
443 updateCardsAttempts.start(nextPollDelaySeconds); | |
444 } | |
445 | |
446 /** | |
447 * Parses JSON response from the notification server, shows notifications and | |
rgustafson
2013/10/04 16:56:47
nit: comma before and
vadimt
2013/10/07 20:29:54
I thought comma is not used before 'and' in a sequ
rgustafson
2013/10/08 18:23:27
FYI: I was taught the exact opposite. The internet
robliao
2013/10/08 20:48:14
A comma is required at the second to last item in
skare_
2013/10/08 23:04:10
+1 for Team Oxford Comma
| |
448 * schedules next update. | |
449 * @param {string} response Server response. | |
450 */ | |
451 function parseAndShowNotificationCards(response) { | |
452 console.log('parseAndShowNotificationCards ' + response); | |
453 var parsedResponse = JSON.parse(response); | |
454 | |
455 var groups = parsedResponse.groups; | |
456 | |
457 // Populate groups with corresponding cards. | |
458 if (parsedResponse.notifications) { | |
459 for (var i = 0; i != parsedResponse.notifications.length; ++i) { | |
460 var card = parsedResponse.notifications[i]; | |
461 var group = groups[card.groupName]; | |
462 group.cards = group.cards || []; | |
463 group.cards.push(card); | |
464 } | |
465 } | |
466 | |
467 instrumented.storage.local.get('notificationGroups', function(items) { | |
468 console.log('parseAndShowNotificationCards-get ' + JSON.stringify(items)); | |
469 items.notificationGroups = items.notificationGroups || {}; | |
470 | |
471 var now = Date.now(); | |
472 | |
473 // Build updated set of groups and merge cards from all groups into one set. | |
474 var updatedGroups = {}; | |
475 var mergedCards = {}; | |
476 | |
477 for (var groupName in groups) { | |
478 var receivedGroup = groups[groupName]; | |
479 var storageGroup = items.notificationGroups[groupName] || { | |
480 cards: [], | |
481 cardsTimestamp: undefined, | |
482 nextPollTime: now, | |
rgustafson
2013/10/04 16:56:47
This seems kind of dangerous if there is a repeati
vadimt
2013/10/07 20:29:54
Every 5 minutes, see MINIMUM_POLLING_PERIOD_SECOND
| |
483 rank: undefined | |
484 }; | |
485 | |
486 if (receivedGroup.requested) | |
487 receivedGroup.cards = receivedGroup.cards || []; | |
488 | |
489 if (receivedGroup.cards) { | |
490 storageGroup.cards = receivedGroup.cards; | |
491 storageGroup.cardsTimestamp = now; | |
492 storageGroup.rank = receivedGroup.rank; | |
493 } | |
494 | |
495 if (receivedGroup.nextPollSeconds !== undefined) { | |
496 storageGroup.nextPollTime = | |
497 now + receivedGroup.nextPollSeconds * MS_IN_SECOND; | |
498 } | |
499 | |
500 updatedGroups[groupName] = storageGroup; | |
501 | |
502 mergeGroup(mergedCards, storageGroup); | |
503 } | |
504 | |
505 scheduleNextPoll(updatedGroups); | |
506 | |
507 chrome.storage.local.set({notificationGroups: updatedGroups}); | |
508 | |
509 showNotificationCards(mergedCards); | |
353 }); | 510 }); |
354 } | 511 } |
355 | 512 |
356 /** | 513 /** |
357 * Requests notification cards from the server. | 514 * Requests notification cards from the server. |
358 * @param {Location} position Location of this computer. | 515 * @param {Location} position Location of this computer. |
359 * @param {function()} callback Completion callback. | 516 */ |
360 */ | 517 function requestNotificationCards(position) { |
361 function requestNotificationCards(position, callback) { | |
362 console.log('requestNotificationCards ' + JSON.stringify(position) + | 518 console.log('requestNotificationCards ' + JSON.stringify(position) + |
363 ' from ' + NOTIFICATION_CARDS_URL); | 519 ' from ' + NOTIFICATION_CARDS_URL); |
364 | 520 |
365 if (!NOTIFICATION_CARDS_URL) { | 521 if (!NOTIFICATION_CARDS_URL) |
366 callback(); | |
367 return; | 522 return; |
368 } | |
369 | 523 |
370 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL); | 524 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_TOTAL); |
371 | 525 |
372 // TODO(vadimt): Should we use 'q' as the parameter name? | 526 instrumented.storage.local.get('notificationGroups', function(items) { |
373 var requestParameters = | 527 console.log('requestNotificationCards-storage-get ' + |
374 'q=' + position.coords.latitude + | 528 JSON.stringify(items)); |
375 ',' + position.coords.longitude + | 529 |
376 ',' + position.coords.accuracy; | 530 var requestParameters = '?timeZoneOffsetMs=' + |
377 | 531 (-new Date().getTimezoneOffset() * MS_IN_MINUTE); |
378 var request = buildServerRequest('notifications', | 532 |
379 'application/x-www-form-urlencoded'); | 533 if (items.notificationGroups) { |
380 | 534 var now = Date.now(); |
381 request.onloadend = function(event) { | 535 |
382 console.log('requestNotificationCards-onloadend ' + request.status); | 536 for (var groupName in items.notificationGroups) { |
383 if (request.status == HTTP_OK) { | 537 var group = items.notificationGroups[groupName]; |
384 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); | 538 if (group.nextPollTime <= now) |
385 parseAndShowNotificationCards(request.response, callback); | 539 requestParameters += ('&requestTypes=' + groupName); |
386 } else { | 540 } |
387 callback(); | 541 } |
388 } | 542 |
389 }; | 543 console.log('requestNotificationCards: request=' + requestParameters); |
390 | 544 |
391 setAuthorization(request, function(success) { | 545 var request = buildServerRequest('GET', |
392 if (success) { | 546 'notifications' + requestParameters); |
393 tasks.debugSetStepName('requestNotificationCards-send-request'); | 547 |
394 request.send(requestParameters); | 548 request.onloadend = function(event) { |
395 } else { | 549 console.log('requestNotificationCards-onloadend ' + request.status); |
396 callback(); | 550 if (request.status == HTTP_OK) { |
397 } | 551 recordEvent(GoogleNowEvent.REQUEST_FOR_CARDS_SUCCESS); |
552 parseAndShowNotificationCards(request.response); | |
553 } | |
554 }; | |
555 | |
556 setAuthorization(request, function(success) { | |
557 if (success) | |
558 request.send(); | |
559 }); | |
398 }); | 560 }); |
399 } | 561 } |
400 | 562 |
401 /** | 563 /** |
402 * Starts getting location for a cards update. | 564 * Starts getting location for a cards update. |
403 */ | 565 */ |
404 function requestLocation() { | 566 function requestLocation() { |
405 console.log('requestLocation'); | 567 console.log('requestLocation'); |
406 recordEvent(GoogleNowEvent.LOCATION_REQUEST); | 568 recordEvent(GoogleNowEvent.LOCATION_REQUEST); |
407 // TODO(vadimt): Figure out location request options. | 569 // TODO(vadimt): Figure out location request options. |
(...skipping 21 matching lines...) Expand all Loading... | |
429 } | 591 } |
430 | 592 |
431 /** | 593 /** |
432 * Obtains new location; requests and shows notification cards based on this | 594 * Obtains new location; requests and shows notification cards based on this |
433 * location. | 595 * location. |
434 * @param {Location} position Location of this computer. | 596 * @param {Location} position Location of this computer. |
435 */ | 597 */ |
436 function updateNotificationsCards(position) { | 598 function updateNotificationsCards(position) { |
437 console.log('updateNotificationsCards ' + JSON.stringify(position) + | 599 console.log('updateNotificationsCards ' + JSON.stringify(position) + |
438 ' @' + new Date()); | 600 ' @' + new Date()); |
439 tasks.add(UPDATE_CARDS_TASK_NAME, function(callback) { | 601 tasks.add(UPDATE_CARDS_TASK_NAME, function() { |
440 console.log('updateNotificationsCards-task-begin'); | 602 console.log('updateNotificationsCards-task-begin'); |
441 updateCardsAttempts.isRunning(function(running) { | 603 updateCardsAttempts.isRunning(function(running) { |
442 if (running) { | 604 if (running) { |
443 updateCardsAttempts.planForNext(function() { | 605 updateCardsAttempts.planForNext(function() { |
444 processPendingDismissals(function(success) { | 606 processPendingDismissals(function(success) { |
445 if (success) { | 607 if (success) { |
446 // The cards are requested only if there are no unsent dismissals. | 608 // The cards are requested only if there are no unsent dismissals. |
447 requestNotificationCards(position, callback); | 609 requestNotificationCards(position); |
448 } else { | |
449 callback(); | |
450 } | 610 } |
451 }); | 611 }); |
452 }); | 612 }); |
453 } | 613 } |
454 }); | 614 }); |
455 }); | 615 }); |
456 } | 616 } |
457 | 617 |
458 /** | 618 /** |
459 * Sends a server request to dismiss a card. | 619 * Sends a server request to dismiss a card. |
460 * @param {string} notificationId Unique identifier of the card. | 620 * @param {string} chromeNotificationId chrome.notifications ID of the card. |
461 * @param {number} dismissalTimeMs Time of the user's dismissal of the card in | 621 * @param {number} dismissalTimeMs Time of the user's dismissal of the card in |
462 * milliseconds since epoch. | 622 * milliseconds since epoch. |
463 * @param {Object} dismissalParameters Dismissal parameters. | 623 * @param {DismissalData} dismissalData Data to build a dismissal request. |
464 * @param {function(boolean)} callbackBoolean Completion callback with 'done' | 624 * @param {function(boolean)} callbackBoolean Completion callback with 'done' |
465 * parameter. | 625 * parameter. |
466 */ | 626 */ |
467 function requestCardDismissal( | 627 function requestCardDismissal( |
468 notificationId, dismissalTimeMs, dismissalParameters, callbackBoolean) { | 628 chromeNotificationId, dismissalTimeMs, dismissalData, callbackBoolean) { |
469 console.log('requestDismissingCard ' + notificationId + ' from ' + | 629 console.log('requestDismissingCard ' + chromeNotificationId + ' from ' + |
470 NOTIFICATION_CARDS_URL); | 630 NOTIFICATION_CARDS_URL); |
471 | 631 |
472 var dismissalAge = Date.now() - dismissalTimeMs; | 632 var dismissalAge = Date.now() - dismissalTimeMs; |
473 | 633 |
474 if (dismissalAge > MAXIMUM_DISMISSAL_AGE_MS) { | 634 if (dismissalAge > MAXIMUM_DISMISSAL_AGE_MS) { |
475 callbackBoolean(true); | 635 callbackBoolean(true); |
476 return; | 636 return; |
477 } | 637 } |
478 | 638 |
479 recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL); | 639 recordEvent(GoogleNowEvent.DISMISS_REQUEST_TOTAL); |
480 var request = buildServerRequest('dismiss', 'application/json'); | 640 |
641 var request = 'notifications/' + dismissalData.notificationId + | |
642 '?age=' + dismissalAge + | |
643 '&chromeNotificationId=' + chromeNotificationId; | |
644 | |
645 for (var paramField in dismissalData.parameters) | |
646 request += ('&' + paramField + '=' + dismissalData.parameters[paramField]); | |
647 | |
648 console.log('requestCardDismissal: request=' + request); | |
649 | |
650 var request = buildServerRequest('DELETE', request); | |
481 request.onloadend = function(event) { | 651 request.onloadend = function(event) { |
482 console.log('requestDismissingCard-onloadend ' + request.status); | 652 console.log('requestDismissingCard-onloadend ' + request.status); |
483 if (request.status == HTTP_OK) | 653 if (request.status == HTTP_NOCONTENT) |
484 recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS); | 654 recordEvent(GoogleNowEvent.DISMISS_REQUEST_SUCCESS); |
485 | 655 |
486 // A dismissal doesn't require further retries if it was successful or | 656 // A dismissal doesn't require further retries if it was successful or |
487 // doesn't have a chance for successful completion. | 657 // doesn't have a chance for successful completion. |
488 var done = request.status == HTTP_OK || | 658 var done = request.status == HTTP_NOCONTENT || |
489 request.status == HTTP_BAD_REQUEST || | 659 request.status == HTTP_BAD_REQUEST || |
490 request.status == HTTP_METHOD_NOT_ALLOWED; | 660 request.status == HTTP_METHOD_NOT_ALLOWED; |
491 callbackBoolean(done); | 661 callbackBoolean(done); |
492 }; | 662 }; |
493 | 663 |
494 setAuthorization(request, function(success) { | 664 setAuthorization(request, function(success) { |
495 if (success) { | 665 if (success) |
496 tasks.debugSetStepName('requestCardDismissal-send-request'); | 666 request.send(); |
497 | 667 else |
498 var dismissalObject = { | |
499 id: notificationId, | |
500 age: dismissalAge, | |
501 dismissal: dismissalParameters | |
502 }; | |
503 request.send(JSON.stringify(dismissalObject)); | |
504 } else { | |
505 callbackBoolean(false); | 668 callbackBoolean(false); |
506 } | |
507 }); | 669 }); |
508 } | 670 } |
509 | 671 |
510 /** | 672 /** |
511 * Tries to send dismiss requests for all pending dismissals. | 673 * Tries to send dismiss requests for all pending dismissals. |
512 * @param {function(boolean)} callbackBoolean Completion callback with 'success' | 674 * @param {function(boolean)} callbackBoolean Completion callback with 'success' |
513 * parameter. Success means that no pending dismissals are left. | 675 * parameter. Success means that no pending dismissals are left. |
514 */ | 676 */ |
515 function processPendingDismissals(callbackBoolean) { | 677 function processPendingDismissals(callbackBoolean) { |
516 tasks.debugSetStepName('processPendingDismissals-storage-get'); | |
517 instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'], | 678 instrumented.storage.local.get(['pendingDismissals', 'recentDismissals'], |
518 function(items) { | 679 function(items) { |
519 console.log('processPendingDismissals-storage-get ' + | 680 console.log('processPendingDismissals-storage-get ' + |
520 JSON.stringify(items)); | 681 JSON.stringify(items)); |
521 items.pendingDismissals = items.pendingDismissals || []; | 682 items.pendingDismissals = items.pendingDismissals || []; |
522 items.recentDismissals = items.recentDismissals || {}; | 683 items.recentDismissals = items.recentDismissals || {}; |
523 | 684 |
524 var dismissalsChanged = false; | 685 var dismissalsChanged = false; |
525 | 686 |
526 function onFinish(success) { | 687 function onFinish(success) { |
(...skipping 10 matching lines...) Expand all Loading... | |
537 if (items.pendingDismissals.length == 0) { | 698 if (items.pendingDismissals.length == 0) { |
538 dismissalAttempts.stop(); | 699 dismissalAttempts.stop(); |
539 onFinish(true); | 700 onFinish(true); |
540 return; | 701 return; |
541 } | 702 } |
542 | 703 |
543 // Send dismissal for the first card, and if successful, repeat | 704 // Send dismissal for the first card, and if successful, repeat |
544 // recursively with the rest. | 705 // recursively with the rest. |
545 var dismissal = items.pendingDismissals[0]; | 706 var dismissal = items.pendingDismissals[0]; |
546 requestCardDismissal( | 707 requestCardDismissal( |
547 dismissal.notificationId, | 708 dismissal.chromeNotificationId, |
548 dismissal.time, | 709 dismissal.time, |
549 dismissal.parameters, | 710 dismissal.dismissalData, |
550 function(done) { | 711 function(done) { |
551 if (done) { | 712 if (done) { |
552 dismissalsChanged = true; | 713 dismissalsChanged = true; |
553 items.pendingDismissals.splice(0, 1); | 714 items.pendingDismissals.splice(0, 1); |
554 items.recentDismissals[dismissal.notificationId] = Date.now(); | 715 items.recentDismissals[dismissal.chromeNotificationId] = |
716 Date.now(); | |
555 doProcessDismissals(); | 717 doProcessDismissals(); |
556 } else { | 718 } else { |
557 onFinish(false); | 719 onFinish(false); |
558 } | 720 } |
559 }); | 721 }); |
560 } | 722 } |
561 | 723 |
562 doProcessDismissals(); | 724 doProcessDismissals(); |
563 }); | 725 }); |
564 } | 726 } |
565 | 727 |
566 /** | 728 /** |
567 * Submits a task to send pending dismissals. | 729 * Submits a task to send pending dismissals. |
568 */ | 730 */ |
569 function retryPendingDismissals() { | 731 function retryPendingDismissals() { |
570 tasks.add(RETRY_DISMISS_TASK_NAME, function(callback) { | 732 tasks.add(RETRY_DISMISS_TASK_NAME, function() { |
571 dismissalAttempts.planForNext(function() { | 733 dismissalAttempts.planForNext(function() { |
572 processPendingDismissals(function(success) { callback(); }); | 734 processPendingDismissals(function(success) {}); |
573 }); | 735 }); |
574 }); | 736 }); |
575 } | 737 } |
576 | 738 |
577 /** | 739 /** |
578 * Opens URL corresponding to the clicked part of the notification. | 740 * Opens URL corresponding to the clicked part of the notification. |
579 * @param {string} notificationId Unique identifier of the notification. | 741 * @param {string} chromeNotificationId chrome.notifications ID of the card. |
580 * @param {function(Object): string} selector Function that extracts the url for | 742 * @param {function(Object): string} selector Function that extracts the url for |
581 * the clicked area from the button action URLs info. | 743 * the clicked area from the button action URLs info. |
582 */ | 744 */ |
583 function onNotificationClicked(notificationId, selector) { | 745 function onNotificationClicked(chromeNotificationId, selector) { |
584 instrumented.storage.local.get('notificationsData', function(items) { | 746 instrumented.storage.local.get('notificationsData', function(items) { |
585 var notificationData = items && | 747 var notificationData = items && |
586 items.notificationsData && | 748 items.notificationsData && |
587 items.notificationsData[notificationId]; | 749 items.notificationsData[chromeNotificationId]; |
588 | 750 |
589 if (!notificationData) | 751 if (!notificationData) |
590 return; | 752 return; |
591 | 753 |
592 var actionUrls = notificationData.actionUrls; | 754 var url = selector(notificationData.actionUrls); |
593 if (typeof actionUrls != 'object') { | |
594 return; | |
595 } | |
596 | |
597 var url = selector(actionUrls); | |
598 if (!url) | 755 if (!url) |
599 return; | 756 return; |
600 | 757 |
601 instrumented.tabs.create({url: url}, function(tab) { | 758 instrumented.tabs.create({url: url}, function(tab) { |
602 if (tab) | 759 if (tab) |
603 chrome.windows.update(tab.windowId, {focused: true}); | 760 chrome.windows.update(tab.windowId, {focused: true}); |
604 else | 761 else |
605 chrome.windows.create({url: url, focused: true}); | 762 chrome.windows.create({url: url, focused: true}); |
606 }); | 763 }); |
607 }); | 764 }); |
(...skipping 12 matching lines...) Expand all Loading... | |
620 // The googlegeolocationaccessenabled preference change callback | 777 // The googlegeolocationaccessenabled preference change callback |
621 // will take care of starting the poll for cards. | 778 // will take care of starting the poll for cards. |
622 } else { | 779 } else { |
623 chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastClickedNo'); | 780 chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastClickedNo'); |
624 onStateChange(); | 781 onStateChange(); |
625 } | 782 } |
626 } | 783 } |
627 | 784 |
628 /** | 785 /** |
629 * Callback for chrome.notifications.onClosed event. | 786 * Callback for chrome.notifications.onClosed event. |
630 * @param {string} notificationId Unique identifier of the notification. | 787 * @param {string} chromeNotificationId chrome.notifications ID of the card. |
631 * @param {boolean} byUser Whether the notification was closed by the user. | 788 * @param {boolean} byUser Whether the notification was closed by the user. |
632 */ | 789 */ |
633 function onNotificationClosed(notificationId, byUser) { | 790 function onNotificationClosed(chromeNotificationId, byUser) { |
634 if (!byUser) | 791 if (!byUser) |
635 return; | 792 return; |
636 | 793 |
637 if (notificationId == WELCOME_TOAST_NOTIFICATION_ID) { | 794 if (chromeNotificationId == WELCOME_TOAST_NOTIFICATION_ID) { |
638 // Even though they only closed the notification without clicking no, treat | 795 // Even though they only closed the notification without clicking no, treat |
639 // it as though they clicked No anwyay, and don't show the toast again. | 796 // it as though they clicked No anwyay, and don't show the toast again. |
640 chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastDismissed'); | 797 chrome.metricsPrivate.recordUserAction('GoogleNow.WelcomeToastDismissed'); |
641 chrome.storage.local.set({userRespondedToToast: true}); | 798 chrome.storage.local.set({userRespondedToToast: true}); |
642 return; | 799 return; |
643 } | 800 } |
644 | 801 |
645 // At this point we are guaranteed that the notification is a now card. | 802 // At this point we are guaranteed that the notification is a now card. |
646 chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed'); | 803 chrome.metricsPrivate.recordUserAction('GoogleNow.Dismissed'); |
647 | 804 |
648 tasks.add(DISMISS_CARD_TASK_NAME, function(callback) { | 805 tasks.add(DISMISS_CARD_TASK_NAME, function() { |
649 dismissalAttempts.start(); | 806 dismissalAttempts.start(); |
650 | 807 |
651 // Deleting the notification in case it was re-added while this task was | 808 // Deleting the notification in case it was re-added while this task was |
652 // scheduled, waiting for execution. | 809 // scheduled, waiting for execution. |
653 cardSet.clear(notificationId); | 810 cardSet.clear(chromeNotificationId); |
654 | 811 |
655 tasks.debugSetStepName('onNotificationClosed-storage-get'); | 812 instrumented.storage.local.get( |
656 instrumented.storage.local.get(['pendingDismissals', 'notificationsData'], | 813 ['pendingDismissals', 'notificationsData'], function(items) { |
657 function(items) { | 814 items.pendingDismissals = items.pendingDismissals || []; |
658 items.pendingDismissals = items.pendingDismissals || []; | 815 items.notificationsData = items.notificationsData || {}; |
659 items.notificationsData = items.notificationsData || {}; | |
660 | 816 |
661 var notificationData = items.notificationsData[notificationId]; | 817 var notificationData = items.notificationsData[chromeNotificationId]; |
662 | 818 |
819 if (notificationData && notificationData.dismissals) { | |
820 for (var i = 0; i < notificationData.dismissals.length; i++) { | |
663 var dismissal = { | 821 var dismissal = { |
664 notificationId: notificationId, | 822 chromeNotificationId: chromeNotificationId, |
665 time: Date.now(), | 823 time: Date.now(), |
666 parameters: notificationData && notificationData.dismissalParameters | 824 dismissalData: notificationData.dismissals[i] |
667 }; | 825 }; |
668 items.pendingDismissals.push(dismissal); | 826 items.pendingDismissals.push(dismissal); |
669 chrome.storage.local.set( | 827 } |
670 {pendingDismissals: items.pendingDismissals}); | 828 |
671 processPendingDismissals(function(success) { callback(); }); | 829 chrome.storage.local.set({pendingDismissals: items.pendingDismissals}); |
672 }); | 830 } |
831 | |
832 processPendingDismissals(function(success) {}); | |
833 }); | |
673 }); | 834 }); |
674 } | 835 } |
675 | 836 |
676 /** | 837 /** |
677 * Initializes the polling system to start monitoring location and fetching | 838 * Initializes the polling system to start monitoring location and fetching |
678 * cards. | 839 * cards. |
679 */ | 840 */ |
680 function startPollingCards() { | 841 function startPollingCards() { |
681 // Create an update timer for a case when for some reason location request | 842 // Create an update timer for a case when for some reason location request |
682 // gets stuck. | 843 // gets stuck. |
(...skipping 226 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
909 var buttons = [{title: 'Yes'}, {title: 'No'}]; | 1070 var buttons = [{title: 'Yes'}, {title: 'No'}]; |
910 var options = { | 1071 var options = { |
911 type: 'basic', | 1072 type: 'basic', |
912 title: 'Enable Google Now Cards', | 1073 title: 'Enable Google Now Cards', |
913 message: 'Would you like to be shown Google Now cards?', | 1074 message: 'Would you like to be shown Google Now cards?', |
914 iconUrl: 'http://www.gstatic.com/googlenow/chrome/default.png', | 1075 iconUrl: 'http://www.gstatic.com/googlenow/chrome/default.png', |
915 priority: 2, | 1076 priority: 2, |
916 buttons: buttons | 1077 buttons: buttons |
917 }; | 1078 }; |
918 instrumented.notifications.create(WELCOME_TOAST_NOTIFICATION_ID, options, | 1079 instrumented.notifications.create(WELCOME_TOAST_NOTIFICATION_ID, options, |
919 function(notificationId) {}); | 1080 function(chromeNotificationId) {}); |
920 } | 1081 } |
921 | 1082 |
922 /** | 1083 /** |
923 * Hides the welcome toast. | 1084 * Hides the welcome toast. |
924 */ | 1085 */ |
925 function hideWelcomeToast() { | 1086 function hideWelcomeToast() { |
926 chrome.notifications.clear( | 1087 instrumented.notifications.clear( |
927 WELCOME_TOAST_NOTIFICATION_ID, | 1088 WELCOME_TOAST_NOTIFICATION_ID, |
928 function() {}); | 1089 function() {}); |
929 } | 1090 } |
930 | 1091 |
931 instrumented.runtime.onInstalled.addListener(function(details) { | 1092 instrumented.runtime.onInstalled.addListener(function(details) { |
932 console.log('onInstalled ' + JSON.stringify(details)); | 1093 console.log('onInstalled ' + JSON.stringify(details)); |
933 if (details.reason != 'chrome_update') { | 1094 if (details.reason != 'chrome_update') { |
934 initialize(); | 1095 initialize(); |
935 } | 1096 } |
936 }); | 1097 }); |
(...skipping 12 matching lines...) Expand all Loading... | |
949 prefValue.value); | 1110 prefValue.value); |
950 onStateChange(); | 1111 onStateChange(); |
951 }); | 1112 }); |
952 | 1113 |
953 authenticationManager.addListener(function() { | 1114 authenticationManager.addListener(function() { |
954 console.log('signIn State Change'); | 1115 console.log('signIn State Change'); |
955 onStateChange(); | 1116 onStateChange(); |
956 }); | 1117 }); |
957 | 1118 |
958 instrumented.notifications.onClicked.addListener( | 1119 instrumented.notifications.onClicked.addListener( |
959 function(notificationId) { | 1120 function(chromeNotificationId) { |
960 chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked'); | 1121 chrome.metricsPrivate.recordUserAction('GoogleNow.MessageClicked'); |
961 onNotificationClicked(notificationId, function(actionUrls) { | 1122 onNotificationClicked(chromeNotificationId, function(actionUrls) { |
962 return actionUrls.messageUrl; | 1123 return actionUrls && actionUrls.messageUrl; |
963 }); | 1124 }); |
964 }); | 1125 }); |
965 | 1126 |
966 instrumented.notifications.onButtonClicked.addListener( | 1127 instrumented.notifications.onButtonClicked.addListener( |
967 function(notificationId, buttonIndex) { | 1128 function(chromeNotificationId, buttonIndex) { |
968 if (notificationId == WELCOME_TOAST_NOTIFICATION_ID) { | 1129 if (chromeNotificationId == WELCOME_TOAST_NOTIFICATION_ID) { |
969 onToastNotificationClicked(buttonIndex); | 1130 onToastNotificationClicked(buttonIndex); |
970 } else { | 1131 } else { |
971 chrome.metricsPrivate.recordUserAction( | 1132 chrome.metricsPrivate.recordUserAction( |
972 'GoogleNow.ButtonClicked' + buttonIndex); | 1133 'GoogleNow.ButtonClicked' + buttonIndex); |
973 onNotificationClicked(notificationId, function(actionUrls) { | 1134 onNotificationClicked(chromeNotificationId, function(actionUrls) { |
974 var url = actionUrls.buttonUrls[buttonIndex]; | 1135 var url = actionUrls.buttonUrls[buttonIndex]; |
975 verify(url, 'onButtonClicked: no url for a button'); | 1136 verify(url, 'onButtonClicked: no url for a button'); |
976 return url; | 1137 return url; |
977 }); | 1138 }); |
978 } | 1139 } |
979 }); | 1140 }); |
980 | 1141 |
981 instrumented.notifications.onClosed.addListener(onNotificationClosed); | 1142 instrumented.notifications.onClosed.addListener(onNotificationClosed); |
982 | 1143 |
983 instrumented.location.onLocationUpdate.addListener(function(position) { | 1144 instrumented.location.onLocationUpdate.addListener(function(position) { |
984 recordEvent(GoogleNowEvent.LOCATION_UPDATE); | 1145 recordEvent(GoogleNowEvent.LOCATION_UPDATE); |
985 updateNotificationsCards(position); | 1146 updateNotificationsCards(position); |
986 }); | 1147 }); |
987 | 1148 |
988 instrumented.omnibox.onInputEntered.addListener(function(text) { | 1149 instrumented.omnibox.onInputEntered.addListener(function(text) { |
989 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; | 1150 localStorage['server_url'] = NOTIFICATION_CARDS_URL = text; |
990 initialize(); | 1151 initialize(); |
991 }); | 1152 }); |
OLD | NEW |