1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // The ChromeNotifierService works together with sync to maintain the state of
6 // user notifications, which can then be presented in the notification center,
7 // via the Notification UI Manager.
9 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
15 #include "base/command_line.h"
16 #include "base/guid.h"
17 #include "base/metrics/histogram.h"
18 #include "base/prefs/pref_service.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/notifications/desktop_notification_service.h"
22 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
23 #include "chrome/browser/notifications/notification.h"
24 #include "chrome/browser/notifications/notification_ui_manager.h"
25 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h"
26 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
27 #include "chrome/browser/notifications/sync_notifier/synced_notification_app_info.h"
28 #include "chrome/browser/notifications/sync_notifier/synced_notification_app_info_service.h"
29 #include "chrome/browser/notifications/sync_notifier/synced_notification_app_info_service_factory.h"
30 #include "chrome/browser/notifications/sync_notifier/welcome_delegate.h"
31 #include "chrome/browser/profiles/profile.h"
32 #include "chrome/common/pref_names.h"
33 #include "components/user_prefs/pref_registry_syncable.h"
34 #include "content/public/browser/browser_thread.h"
35 #include "content/public/browser/user_metrics.h"
36 #include "grit/generated_resources.h"
37 #include "grit/theme_resources.h"
38 #include "sync/api/sync_change.h"
39 #include "sync/api/sync_change_processor.h"
40 #include "sync/api/sync_error_factory.h"
41 #include "sync/protocol/sync.pb.h"
42 #include "sync/protocol/synced_notification_specifics.pb.h"
43 #include "third_party/WebKit/public/web/WebTextDirection.h"
44 #include "ui/base/l10n/l10n_util.h"
45 #include "ui/base/resource/resource_bundle.h"
46 #include "ui/message_center/notifier_settings.h"
49 using base::UserMetricsAction;
52 const char kFirstSyncedNotificationServiceId[] = "Google+";
53 const char kSyncedNotificationsWelcomeOrigin[] =
54 "synced-notifications://welcome";
56 bool ChromeNotifierService::avoid_bitmap_fetching_for_test_ = false;
58 ChromeNotifierService::ChromeNotifierService(Profile* profile,
59 NotificationUIManager* manager)
61 notification_manager_(manager),
62 synced_notification_first_run_(false) {
66 // Get a pointer to the app info service so we can get app info data.
67 synced_notification_app_info_service_ =
68 SyncedNotificationAppInfoServiceFactory::GetForProfile(
69 profile_, Profile::IMPLICIT_ACCESS);
71 DCHECK(synced_notification_app_info_service_ != NULL);
73 synced_notification_app_info_service_->set_chrome_notifier_service(this);
77 ChromeNotifierService::~ChromeNotifierService() {
78 if (synced_notification_app_info_service_)
79 synced_notification_app_info_service_->set_chrome_notifier_service(NULL);
82 // Methods from KeyedService.
83 void ChromeNotifierService::Shutdown() {}
85 // syncer::SyncableService implementation.
87 // This is called at startup to sync with the server.
88 // This code is not thread safe.
89 syncer::SyncMergeResult ChromeNotifierService::MergeDataAndStartSyncing(
90 syncer::ModelType type,
91 const syncer::SyncDataList& initial_sync_data,
92 scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
93 scoped_ptr<syncer::SyncErrorFactory> error_handler) {
94 thread_checker_.CalledOnValidThread();
95 DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
96 syncer::SyncMergeResult merge_result(syncer::SYNCED_NOTIFICATIONS);
97 // A list of local changes to send up to the sync server.
98 syncer::SyncChangeList new_changes;
99 sync_processor_ = sync_processor.Pass();
101 for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
102 it != initial_sync_data.end(); ++it) {
103 const syncer::SyncData& sync_data = *it;
104 DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, sync_data.GetDataType());
106 // Build a local notification object from the sync data.
107 scoped_ptr<SyncedNotification> incoming(CreateNotificationFromSyncData(
114 // Process each incoming remote notification.
115 const std::string& key = incoming->GetKey();
116 DCHECK_GT(key.length(), 0U);
117 SyncedNotification* found = FindNotificationById(key);
120 // If there are no conflicts, copy in the data from remote.
121 Add(incoming.Pass());
123 // If the incoming (remote) and stored (local) notifications match
124 // in all fields, we don't need to do anything here.
125 if (incoming->EqualsIgnoringReadState(*found)) {
127 if (incoming->GetReadState() == found->GetReadState()) {
128 // Notification matches on the client and the server, nothing to do.
131 // If the read state is different, read wins for both places.
132 if (incoming->GetReadState() == SyncedNotification::kDismissed) {
133 // If it is marked as read on the server, but not the client.
134 found->NotificationHasBeenDismissed();
135 // Tell the Notification UI Manager to remove it.
136 notification_manager_->CancelById(found->GetKey());
137 } else if (incoming->GetReadState() == SyncedNotification::kRead) {
138 // If it is marked as read on the server, but not the client.
139 found->NotificationHasBeenRead();
140 // Tell the Notification UI Manager to remove it.
141 notification_manager_->CancelById(found->GetKey());
143 // If it is marked as read on the client, but not the server.
144 syncer::SyncData sync_data = CreateSyncDataFromNotification(*found);
145 new_changes.push_back(
146 syncer::SyncChange(FROM_HERE,
147 syncer::SyncChange::ACTION_UPDATE,
150 // If local state changed, notify Notification UI Manager.
153 // If different, just replace the local with the remote.
154 // TODO(petewil): Someday we may allow changes from the client to
155 // flow upwards, when we do, we will need better merge resolution.
156 found->Update(sync_data);
158 // Tell the notification manager to update the notification.
159 UpdateInMessageCenter(found);
164 // Send up the changes that were made locally.
165 if (new_changes.size() > 0) {
166 merge_result.set_error(sync_processor_->ProcessSyncChanges(
167 FROM_HERE, new_changes));
170 // Once we complete our first sync, we mark "first run" as false,
171 // subsequent runs of Synced Notifications will get normal treatment.
172 if (synced_notification_first_run_) {
173 synced_notification_first_run_ = false;
174 profile_->GetPrefs()->SetBoolean(prefs::kSyncedNotificationFirstRun, false);
180 void ChromeNotifierService::StopSyncing(syncer::ModelType type) {
181 DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
182 // Since this data type is not user-unselectable, we chose not to implement
183 // the stop syncing method, and instead do nothing here.
186 syncer::SyncDataList ChromeNotifierService::GetAllSyncData(
187 syncer::ModelType type) const {
188 DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
189 syncer::SyncDataList sync_data;
191 // Copy our native format data into a SyncDataList format.
192 ScopedVector<SyncedNotification>::const_iterator it =
193 notification_data_.begin();
194 for (; it != notification_data_.end(); ++it) {
195 sync_data.push_back(CreateSyncDataFromNotification(**it));
201 // This method is called when there is an incoming sync change from the server.
202 syncer::SyncError ChromeNotifierService::ProcessSyncChanges(
203 const tracked_objects::Location& from_here,
204 const syncer::SyncChangeList& change_list) {
205 thread_checker_.CalledOnValidThread();
206 syncer::SyncError error;
208 for (syncer::SyncChangeList::const_iterator it = change_list.begin();
209 it != change_list.end(); ++it) {
210 syncer::SyncData sync_data = it->sync_data();
211 DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, sync_data.GetDataType());
212 syncer::SyncChange::SyncChangeType change_type = it->change_type();
214 scoped_ptr<SyncedNotification> new_notification(
215 CreateNotificationFromSyncData(sync_data));
216 if (!new_notification.get()) {
217 NOTREACHED() << "Failed to read notification.";
221 const std::string& key = new_notification->GetKey();
222 DCHECK_GT(key.length(), 0U);
223 SyncedNotification* found = FindNotificationById(key);
225 switch (change_type) {
226 case syncer::SyncChange::ACTION_ADD:
227 // Intentional fall through, cases are identical.
228 case syncer::SyncChange::ACTION_UPDATE:
230 Add(new_notification.Pass());
233 // Update it in our store.
234 found->Update(sync_data);
235 // Tell the notification manager to update the notification.
236 UpdateInMessageCenter(found);
239 case syncer::SyncChange::ACTION_DELETE:
243 // Remove it from our store.
244 FreeNotificationById(key);
245 // Remove it from the message center.
246 UpdateInMessageCenter(new_notification.get());
247 // TODO(petewil): Do I need to remember that it was deleted in case the
248 // add arrives after the delete? If so, how long do I need to remember?
260 // Support functions for data type conversion.
262 // Static method. Get to the sync data in our internal format.
263 syncer::SyncData ChromeNotifierService::CreateSyncDataFromNotification(
264 const SyncedNotification& notification) {
265 // Construct the sync_data using the specifics from the notification.
266 return syncer::SyncData::CreateLocalData(
267 notification.GetKey(), notification.GetKey(),
268 notification.GetEntitySpecifics());
271 // Convert from SyncData to our internal format.
272 scoped_ptr<SyncedNotification>
273 ChromeNotifierService::CreateNotificationFromSyncData(
274 const syncer::SyncData& sync_data) {
275 // Get a pointer to our data within the sync_data object.
276 sync_pb::SyncedNotificationSpecifics specifics =
277 sync_data.GetSpecifics().synced_notification();
279 // Check for mandatory fields in the sync_data object.
280 if (!specifics.has_coalesced_notification() ||
281 !specifics.coalesced_notification().has_key() ||
282 !specifics.coalesced_notification().has_read_state()) {
283 DVLOG(1) << "Synced Notification missing mandatory fields "
284 << "has coalesced notification? "
285 << specifics.has_coalesced_notification()
286 << " has key? " << specifics.coalesced_notification().has_key()
287 << " has read state? "
288 << specifics.coalesced_notification().has_read_state();
289 return scoped_ptr<SyncedNotification>();
292 bool is_well_formed_unread_notification =
293 (static_cast<SyncedNotification::ReadState>(
294 specifics.coalesced_notification().read_state()) ==
295 SyncedNotification::kUnread &&
296 specifics.coalesced_notification().has_render_info());
297 bool is_well_formed_read_notification =
298 (static_cast<SyncedNotification::ReadState>(
299 specifics.coalesced_notification().read_state()) ==
300 SyncedNotification::kRead);
301 bool is_well_formed_dismissed_notification =
302 (static_cast<SyncedNotification::ReadState>(
303 specifics.coalesced_notification().read_state()) ==
304 SyncedNotification::kDismissed);
306 // If the notification is poorly formed, return a null pointer.
307 if (!is_well_formed_unread_notification &&
308 !is_well_formed_read_notification &&
309 !is_well_formed_dismissed_notification) {
310 DVLOG(1) << "Synced Notification is not well formed."
311 << " unread well formed? "
312 << is_well_formed_unread_notification
313 << " dismissed well formed? "
314 << is_well_formed_dismissed_notification
315 << " read well formed? "
316 << is_well_formed_read_notification;
317 return scoped_ptr<SyncedNotification>();
320 // Create a new notification object based on the supplied sync_data.
321 scoped_ptr<SyncedNotification> notification(
322 new SyncedNotification(sync_data, this, notification_manager_));
324 return notification.Pass();
327 // This returns a pointer into a vector that we own. Caller must not free it.
328 // Returns NULL if no match is found.
329 SyncedNotification* ChromeNotifierService::FindNotificationById(
330 const std::string& notification_id) {
331 // TODO(petewil): We can make a performance trade off here.
332 // While the vector has good locality of reference, a map has faster lookup.
333 // Based on how big we expect this to get, maybe change this to a map.
334 ScopedVector<SyncedNotification>::const_iterator it =
335 notification_data_.begin();
336 for (; it != notification_data_.end(); ++it) {
337 SyncedNotification* notification = *it;
338 if (notification_id == notification->GetKey())
345 void ChromeNotifierService::FreeNotificationById(
346 const std::string& notification_id) {
347 ScopedVector<SyncedNotification>::iterator it = notification_data_.begin();
348 for (; it != notification_data_.end(); ++it) {
349 SyncedNotification* notification = *it;
350 if (notification_id == notification->GetKey()) {
351 notification_data_.erase(it);
357 void ChromeNotifierService::GetSyncedNotificationServices(
358 std::vector<message_center::Notifier*>* notifiers) {
359 // TODO(mukai|petewil): Check the profile's eligibility before adding the
361 std::vector<SyncedNotificationSendingServiceSettingsData>
362 sending_service_settings_data;
364 if (synced_notification_app_info_service_ == NULL)
367 sending_service_settings_data =
368 synced_notification_app_info_service_->GetAllSendingServiceSettingsData();
370 for (size_t ii = 0; ii < sending_service_settings_data.size(); ++ii) {
372 // Enable or disable the sending service per saved preferences.
373 bool app_enabled = false;
374 std::set<std::string>::iterator iter;
375 iter = find(enabled_sending_services_.begin(),
376 enabled_sending_services_.end(),
377 sending_service_settings_data[ii].notifier_id.id);
378 app_enabled = iter != enabled_sending_services_.end();
380 message_center::Notifier* app_info_notifier = new message_center::Notifier(
381 sending_service_settings_data[ii].notifier_id,
383 sending_service_settings_data[ii].settings_display_name),
386 app_info_notifier->icon = sending_service_settings_data[ii].settings_icon;
388 // |notifiers| takes ownership of |app_info_notifier|.
389 notifiers->push_back(app_info_notifier);
393 void ChromeNotifierService::OnAddedAppIds(
394 std::vector<std::string> added_app_ids) {
396 std::vector<std::string>::const_iterator app_id_iter;
397 for (app_id_iter = added_app_ids.begin(); app_id_iter != added_app_ids.end();
399 // Make sure this is not a dup, if it is, do nothing.
400 // TODO(petewil): consider a case insensitive compare.
401 std::set<std::string>::iterator sending_service_iter;
402 sending_service_iter = enabled_sending_services_.find(*app_id_iter);
403 if (sending_service_iter != enabled_sending_services_.end())
406 // Find any newly enabled notifications and call display on them.
407 // Show the welcome toast if required.
408 ScopedVector<SyncedNotification>::iterator notification_iter;
409 for (notification_iter = notification_data_.begin();
410 notification_iter != notification_data_.end();
411 ++notification_iter) {
412 (*notification_iter)->ShowAllForAppId(profile_, *app_id_iter);
417 void ChromeNotifierService::OnRemovedAppIds(
418 std::vector<std::string> removed_app_ids) {
419 // Remove from enabled sending services.
420 // Don't remove from initialized sending services. If it gets re-added later,
421 // we want to remember the user's decision, so we also leave prefs intact.
423 // Find any displayed notifications and remove them from the notification
425 std::vector<std::string>::const_iterator app_id_iter;
426 for (app_id_iter = removed_app_ids.begin();
427 app_id_iter != removed_app_ids.end();
429 // Find any newly disabled notifications and remove them.
430 ScopedVector<SyncedNotification>::iterator notification_iter;
431 for (notification_iter = notification_data_.begin();
432 notification_iter != notification_data_.end();
433 ++notification_iter) {
434 (*notification_iter)->HideAllForAppId(*app_id_iter);
439 void ChromeNotifierService::MarkNotificationAsRead(
440 const std::string& key) {
441 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
442 SyncedNotification* notification = FindNotificationById(key);
443 CHECK(notification != NULL);
445 notification->NotificationHasBeenRead();
446 syncer::SyncChangeList new_changes;
448 syncer::SyncData sync_data = CreateSyncDataFromNotification(*notification);
449 new_changes.push_back(
450 syncer::SyncChange(FROM_HERE,
451 syncer::SyncChange::ACTION_UPDATE,
454 // Send up the changes that were made locally.
455 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
458 // Add a new notification to our data structure. This takes ownership
459 // of the passed in pointer.
460 void ChromeNotifierService::Add(scoped_ptr<SyncedNotification> notification) {
461 SyncedNotification* notification_copy = notification.get();
462 // Take ownership of the object and put it into our local storage.
463 notification_data_.push_back(notification.release());
465 // If the user is not interested in this type of notification, ignore it.
466 std::string sending_service_id = GetSendingServiceId(notification_copy);
467 std::set<std::string>::iterator iter = find(enabled_sending_services_.begin(),
468 enabled_sending_services_.end(),
470 if (iter == enabled_sending_services_.end()) {
471 iter = find(initialized_sending_services_.begin(),
472 initialized_sending_services_.end(),
474 if (iter != initialized_sending_services_.end())
478 UpdateInMessageCenter(notification_copy);
481 std::string ChromeNotifierService::GetSendingServiceId(
482 const SyncedNotification* synced_notification) {
483 // Get the App Id from the notification, and look it up in the synced
484 // notification app info service.
485 std::string app_id = synced_notification->GetAppId();
487 DCHECK(synced_notification_app_info_service_ != NULL);
489 return synced_notification_app_info_service_->FindSendingServiceNameFromAppId(
493 void ChromeNotifierService::AddForTest(
494 scoped_ptr<notifier::SyncedNotification> notification) {
495 notification_data_.push_back(notification.release());
498 void ChromeNotifierService::SetSyncedNotificationAppInfoServiceForTest(
499 SyncedNotificationAppInfoService* synced_notification_app_info_service) {
500 // If there already is a service attached, clear their reference to us.
501 if (synced_notification_app_info_service_)
502 synced_notification_app_info_service_->set_chrome_notifier_service(NULL);
504 synced_notification_app_info_service_ = synced_notification_app_info_service;
505 synced_notification_app_info_service_->set_chrome_notifier_service(this);
508 void ChromeNotifierService::UpdateInMessageCenter(
509 SyncedNotification* notification) {
510 // If the feature is disabled, exit now.
511 if (!notifier::ChromeNotifierServiceFactory::UseSyncedNotifications(
512 CommandLine::ForCurrentProcess()))
515 notification->LogNotification();
517 if (notification->GetReadState() == SyncedNotification::kUnread) {
518 // If the message is unread, update it.
519 Display(notification);
521 // If the message is read or deleted, dismiss it from the center.
522 // We intentionally ignore errors if it is not in the center.
523 notification_manager_->CancelById(notification->GetKey());
527 void ChromeNotifierService::Display(SyncedNotification* notification) {
528 // If this is the first run for the feature, don't surprise the user.
529 // Instead, place all backlogged notifications into the notification
531 if (synced_notification_first_run_) {
532 // Setting the toast state to false will prevent toasting the notification.
533 notification->set_toast_state(false);
536 // Our tests cannot use the network for reliability reasons.
537 if (avoid_bitmap_fetching_for_test_) {
538 notification->Show(profile_);
542 // Set up to fetch the bitmaps.
543 notification->QueueBitmapFetchJobs(this, profile_);
545 // Start the bitmap fetching, Show() will be called when the last bitmap
546 // either arrives or times out.
547 notification->StartBitmapFetch();
550 void ChromeNotifierService::OnSyncedNotificationServiceEnabled(
551 const std::string& notifier_id, bool enabled) {
552 std::set<std::string>::iterator iter;
554 // Make a copy of the notifier_id, which might not have lifetime long enough
555 // for this function to finish all of its work.
556 std::string notifier_id_copy(notifier_id);
558 iter = find(enabled_sending_services_.begin(),
559 enabled_sending_services_.end(),
562 base::ListValue synced_notification_services;
564 // Add the notifier_id if it is enabled and not already there.
565 if (iter == enabled_sending_services_.end() && enabled) {
566 enabled_sending_services_.insert(notifier_id_copy);
567 // Check now for any outstanding notifications.
568 DisplayUnreadNotificationsFromSource(notifier_id);
569 BuildServiceListValueInplace(enabled_sending_services_,
570 &synced_notification_services);
571 // Add this preference to the enabled list.
572 profile_->GetPrefs()->Set(prefs::kEnabledSyncedNotificationSendingServices,
573 synced_notification_services);
574 // Remove the notifier_id if it is disabled and present.
575 } else if (iter != enabled_sending_services_.end() && !enabled) {
576 enabled_sending_services_.erase(iter);
577 BuildServiceListValueInplace(enabled_sending_services_,
578 &synced_notification_services);
579 // Remove this peference from the enabled list.
580 profile_->GetPrefs()->Set(prefs::kEnabledSyncedNotificationSendingServices,
581 synced_notification_services);
582 RemoveUnreadNotificationsFromSource(notifier_id_copy);
585 // Collect UMA statistics when a service is enabled or disabled.
587 content::RecordAction(
588 UserMetricsAction("SyncedNotifications.SendingServiceEnabled"));
590 content::RecordAction(
591 UserMetricsAction("SyncedNotifications.SendingServiceDisabled"));
594 // Collect individual service enabling/disabling statistics.
595 CollectPerServiceEnablingStatistics(notifier_id, enabled);
600 void ChromeNotifierService::CollectPerServiceEnablingStatistics(
601 const std::string& notifier_id,
603 // TODO(petewil) - This approach does not scale well as we add new services,
604 // but we are limited to using predefined ENUM values in histogram based UMA
605 // data, which does not permit arbitrary strings.
606 // Find a way to make it scale, or remove enum value this when we have enough
609 ChromeNotifierServiceActionType action =
610 CHROME_NOTIFIER_SERVICE_ACTION_UNKNOWN;
612 // Derive action type from notifier_id and enabled.
613 // TODO(petewil): Add more sending services as they are enabled.
614 if (notifier_id == std::string(kFirstSyncedNotificationServiceId)) {
616 ? CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_ENABLED
617 : CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_DISABLED;
620 UMA_HISTOGRAM_ENUMERATION("ChromeNotifierService.Actions",
622 CHROME_NOTIFIER_SERVICE_ACTION_COUNT);
625 void ChromeNotifierService::BuildServiceListValueInplace(
626 std::set<std::string> services, base::ListValue* list_value) {
627 std::set<std::string>::iterator iter;
629 // Iterate over the strings, adding each one to the list value
630 for (iter = services.begin();
631 iter != services.end();
633 base::StringValue* string_value(new base::StringValue(*iter));
634 list_value->Append(string_value);
638 void ChromeNotifierService::DisplayUnreadNotificationsFromSource(
639 const std::string& notifier_id) {
640 for (std::vector<SyncedNotification*>::const_iterator iter =
641 notification_data_.begin();
642 iter != notification_data_.end();
644 if (GetSendingServiceId(*iter) == notifier_id &&
645 (*iter)->GetReadState() == SyncedNotification::kUnread)
650 void ChromeNotifierService::RemoveUnreadNotificationsFromSource(
651 const std::string& notifier_id) {
652 for (std::vector<SyncedNotification*>::const_iterator iter =
653 notification_data_.begin();
654 iter != notification_data_.end();
656 if (GetSendingServiceId(*iter) == notifier_id &&
657 (*iter)->GetReadState() == SyncedNotification::kUnread) {
658 notification_manager_->CancelById((*iter)->GetKey());
663 void ChromeNotifierService::OnEnabledSendingServiceListPrefChanged(
664 std::set<std::string>* ids_field) {
666 const std::vector<std::string> pref_list =
667 enabled_sending_services_prefs_.GetValue();
668 for (size_t i = 0; i < pref_list.size(); ++i) {
669 std::string element = pref_list[i];
670 if (!element.empty())
671 ids_field->insert(element);
673 LOG(WARNING) << i << "-th element is not a string "
674 << prefs::kEnabledSyncedNotificationSendingServices;
678 void ChromeNotifierService::OnInitializedSendingServiceListPrefChanged(
679 std::set<std::string>* ids_field) {
681 const std::vector<std::string> pref_list =
682 initialized_sending_services_prefs_.GetValue();
683 for (size_t i = 0; i < pref_list.size(); ++i) {
684 std::string element = pref_list[i];
685 if (!element.empty())
686 ids_field->insert(element);
688 LOG(WARNING) << i << "-th element is not a string for "
689 << prefs::kInitializedSyncedNotificationSendingServices;
693 void ChromeNotifierService::OnSyncedNotificationFirstRunBooleanPrefChanged(
695 synced_notification_first_run_ = *new_value;
698 void ChromeNotifierService::RegisterProfilePrefs(
699 user_prefs::PrefRegistrySyncable* registry) {
700 // Register the pref for the list of enabled services.
701 registry->RegisterListPref(
702 prefs::kEnabledSyncedNotificationSendingServices,
703 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
704 // Register the pref for the list of initialized services.
705 registry->RegisterListPref(
706 prefs::kInitializedSyncedNotificationSendingServices,
707 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
708 // Register the preference for first run status, defaults to "true",
709 // meaning that this is the first run of the Synced Notification feature.
710 registry->RegisterBooleanPref(
711 prefs::kSyncedNotificationFirstRun, true,
712 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
715 void ChromeNotifierService::InitializePrefs() {
716 // Set up any pref changes to update our list of services.
717 enabled_sending_services_prefs_.Init(
718 prefs::kEnabledSyncedNotificationSendingServices,
719 profile_->GetPrefs(),
721 &ChromeNotifierService::OnEnabledSendingServiceListPrefChanged,
722 base::Unretained(this),
723 base::Unretained(&enabled_sending_services_)));
724 initialized_sending_services_prefs_.Init(
725 prefs::kInitializedSyncedNotificationSendingServices,
726 profile_->GetPrefs(),
728 &ChromeNotifierService::OnInitializedSendingServiceListPrefChanged,
729 base::Unretained(this),
730 base::Unretained(&initialized_sending_services_)));
731 synced_notification_first_run_prefs_.Init(
732 prefs::kSyncedNotificationFirstRun,
733 profile_->GetPrefs(),
735 &ChromeNotifierService::
736 OnSyncedNotificationFirstRunBooleanPrefChanged,
737 base::Unretained(this),
738 base::Unretained(&synced_notification_first_run_)));
740 // Get the prefs from last session into our memeber varilables
741 OnEnabledSendingServiceListPrefChanged(&enabled_sending_services_);
742 OnInitializedSendingServiceListPrefChanged(&initialized_sending_services_);
744 synced_notification_first_run_ =
745 profile_->GetPrefs()->GetBoolean(prefs::kSyncedNotificationFirstRun);
748 void ChromeNotifierService::ShowWelcomeToastIfNecessary(
749 const SyncedNotification* synced_notification,
750 NotificationUIManager* notification_ui_manager) {
751 const std::string& sending_service_id =
752 GetSendingServiceId(synced_notification);
754 std::set<std::string>::iterator iter;
755 iter = find(initialized_sending_services_.begin(),
756 initialized_sending_services_.end(),
759 // If we already initialized the sending service, then return early since no
760 // welcome toast is necessary.
761 if (iter != initialized_sending_services_.end())
764 // If there is no app info, we can't show a welcome toast. All synced
765 // notifications will be delayed until an app_info data structure can be
766 // constructed for them.
767 notifier::SyncedNotificationAppInfo* app_info =
768 FindAppInfoByAppId(synced_notification->GetAppId());
772 if (app_info->settings_icon_url().is_empty()) {
773 gfx::Image notification_app_icon = synced_notification->GetAppIcon();
774 if (notification_app_icon.IsEmpty()) {
775 // This block should only be reached in tests since the downloads are
776 // already finished for |synced_notification|.
777 DVLOG(1) << "Unable to find the app icon for the welcome notification. "
778 << "Service ID: " << sending_service_id;
782 message_center::NotifierId notifier_id(
783 message_center::NotifierId::SYNCED_NOTIFICATION_SERVICE,
786 Notification notification = CreateWelcomeNotificationForService(app_info);
787 notification_ui_manager->Add(notification, profile_);
789 enabled_sending_services_.insert(sending_service_id);
790 initialized_sending_services_.insert(sending_service_id);
792 // Build a ListValue with the list of services to be enabled.
793 base::ListValue enabled_sending_services;
794 base::ListValue initialized_sending_services;
796 // Mark any new services as enabled in preferences.
797 BuildServiceListValueInplace(enabled_sending_services_,
798 &enabled_sending_services);
799 profile_->GetPrefs()->Set(prefs::kEnabledSyncedNotificationSendingServices,
800 enabled_sending_services);
801 // Mark any new services as initialized in preferences.
802 BuildServiceListValueInplace(initialized_sending_services_,
803 &initialized_sending_services);
804 profile_->GetPrefs()->Set(
805 prefs::kInitializedSyncedNotificationSendingServices,
806 initialized_sending_services);
809 notifier::SyncedNotificationAppInfo* ChromeNotifierService::FindAppInfoByAppId(
810 const std::string& app_id) const {
811 if (NULL == synced_notification_app_info_service_)
814 return synced_notification_app_info_service_->
815 FindSyncedNotificationAppInfoByAppId(app_id);
818 const Notification ChromeNotifierService::CreateWelcomeNotificationForService(
819 SyncedNotificationAppInfo* app_info) {
820 std::string welcome_notification_id = base::GenerateGUID();
821 message_center::NotifierId notifier_id = app_info->GetNotifierId();
822 scoped_refptr<WelcomeDelegate> delegate(
823 new WelcomeDelegate(welcome_notification_id,
826 app_info->welcome_link_url()));
828 message_center::ButtonInfo button_info(
829 l10n_util::GetStringUTF16(IDS_NOTIFIER_WELCOME_BUTTON));
830 button_info.icon = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
831 IDR_NOTIFIER_BLOCK_BUTTON);
833 message_center::RichNotificationData rich_notification_data;
834 rich_notification_data.buttons.push_back(button_info);
836 message_center::NOTIFICATION_TYPE_BASE_FORMAT,
837 GURL(kSyncedNotificationsWelcomeOrigin),
838 base::UTF8ToUTF16(app_info->settings_display_name()),
839 l10n_util::GetStringUTF16(IDS_NOTIFIER_WELCOME_BODY),
841 blink::WebTextDirectionDefault,
843 l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_DISPLAY_SOURCE),
844 base::UTF8ToUTF16(welcome_notification_id),
845 rich_notification_data,
849 } // namespace notifier