chrome_notifier_service.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
12a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
22a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
32a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// found in the LICENSE file.
42a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
52a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// The ChromeNotifierService works together with sync to maintain the state of
62a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// user notifications, which can then be presented in the notification center,
72a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// via the Notification UI Manager.
82a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
92a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include <set>
12a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include <string>
13a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include <vector>
14a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
152385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch#include "base/command_line.h"
165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/guid.h"
178bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "base/metrics/histogram.h"
18d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/prefs/pref_service.h"
195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
20d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "base/values.h"
21eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/notifications/desktop_notification_service.h"
22eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "chrome/browser/notifications/desktop_notification_service_factory.h"
232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/notifications/notification.h"
242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/notifications/notification_ui_manager.h"
255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/notifications/sync_notifier/chrome_notifier_delegate.h"
262385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "chrome/browser/notifications/sync_notifier/welcome_delegate.h"
282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
29d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "chrome/common/pref_names.h"
30d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)#include "components/user_prefs/pref_registry_syncable.h"
31a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
328bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)#include "content/public/browser/user_metrics.h"
33a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "grit/generated_resources.h"
34a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "grit/theme_resources.h"
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/api/sync_change.h"
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/api/sync_change_processor.h"
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/api/sync_error_factory.h"
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/protocol/sync.pb.h"
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "sync/protocol/synced_notification_specifics.pb.h"
407d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "third_party/WebKit/public/web/WebTextDirection.h"
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "ui/base/l10n/l10n_util.h"
42a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch#include "ui/base/resource/resource_bundle.h"
43eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "ui/message_center/notifier_settings.h"
44eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "url/gurl.h"
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)using base::UserMetricsAction;
478bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)namespace notifier {
49a3f7b4e666c476898878fa745f637129375cd889Ben Murdochconst char kFirstSyncedNotificationServiceId[] = "Google+";
505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const char kSyncedNotificationsWelcomeOrigin[] =
515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    "synced-notifications://welcome";
525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// SyncedNotificationAppInfo is a class that contains the information necessary
545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// to produce a welcome notification and the app badges for all synced
555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// notification.
565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)// TODO(dewittj): Convert this into a sync protobuf-backed data structure.
575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)class SyncedNotificationAppInfo {
585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) public:
595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  explicit SyncedNotificationAppInfo(const std::string& app_id,
605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                                     const base::string16& service_name);
615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ~SyncedNotificationAppInfo();
625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& app_id() const { return app_id_; }
645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const base::string16& service_name() const { return service_name_; }
655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const base::string16& title() const { return title_; }
665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void set_icon(const gfx::Image& icon) { icon_ = icon; }
685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const gfx::Image& icon() const { return icon_; }
695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  void set_small_icon(const gfx::Image& small_icon) {
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    small_icon_ = small_icon;
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const gfx::Image& small_icon() const { return small_icon_; }
745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const message_center::NotifierId GetNotifierId() const;
765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles) private:
785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string app_id_;
795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 service_name_;
805d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  gfx::Image icon_;
815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  gfx::Image small_icon_;
825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 title_;
835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::string16 message_;
845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)};
855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SyncedNotificationAppInfo::SyncedNotificationAppInfo(
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& app_id,
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const base::string16& service_name)
895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : app_id_(app_id), service_name_(service_name) {
905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  title_ =
915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      l10n_util::GetStringFUTF16(IDS_NOTIFIER_WELCOME_TITLE, service_name_);
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SyncedNotificationAppInfo::~SyncedNotificationAppInfo() {}
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const message_center::NotifierId SyncedNotificationAppInfo::GetNotifierId()
975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const {
985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return message_center::NotifierId(
995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      message_center::NotifierId::SYNCED_NOTIFICATION_SERVICE, app_id());
1005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
101eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
102868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)bool ChromeNotifierService::avoid_bitmap_fetching_for_test_ = false;
103868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)
1042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeNotifierService::ChromeNotifierService(Profile* profile,
1052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                             NotificationUIManager* manager)
1065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    : profile_(profile),
1075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      notification_manager_(manager),
1084e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      synced_notification_first_run_(false) {
1095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // TODO(petewil): Replace this temporary hardcoding with a new sync datatype
1105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // to dynamically get the name and icon for each synced notification sending
1115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // service.  Until then, we use hardcoded service icons for all services.
1125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // crbug.com/248337
1135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncedNotificationAppInfo* temp_app_info = new SyncedNotificationAppInfo(
1145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      kFirstSyncedNotificationServiceId,
1155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      l10n_util::GetStringUTF16(IDS_FIRST_SYNCED_NOTIFICATION_SERVICE_NAME));
1165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  temp_app_info->set_small_icon(
1175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      ui::ResourceBundle::GetSharedInstance().GetImageNamed(
1185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)          IDR_TEMPORARY_GOOGLE_PLUS_ICON));
1195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  app_info_data_.push_back(temp_app_info);
1205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
121d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  InitializePrefs();
122a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch}
1235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ChromeNotifierService::~ChromeNotifierService() {}
1252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
12690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)// Methods from BrowserContextKeyedService.
127868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)void ChromeNotifierService::Shutdown() {}
1282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// syncer::SyncableService implementation.
1302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This is called at startup to sync with the server.
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This code is not thread safe.
1332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncMergeResult ChromeNotifierService::MergeDataAndStartSyncing(
1345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    syncer::ModelType type,
1355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncDataList& initial_sync_data,
1365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<syncer::SyncChangeProcessor> sync_processor,
1375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    scoped_ptr<syncer::SyncErrorFactory> error_handler) {
138a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
1392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncMergeResult merge_result(syncer::SYNCED_NOTIFICATIONS);
1412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // A list of local changes to send up to the sync server.
1422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList new_changes;
1432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_processor_ = sync_processor.Pass();
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (syncer::SyncDataList::const_iterator it = initial_sync_data.begin();
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != initial_sync_data.end(); ++it) {
1472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const syncer::SyncData& sync_data = *it;
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, sync_data.GetDataType());
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Build a local notification object from the sync data.
1512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<SyncedNotification> incoming(CreateNotificationFromSyncData(
1522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        sync_data));
153b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    if (!incoming) {
154b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // TODO(petewil): Turn this into a NOTREACHED() call once we fix the
155b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      // underlying problem causing bad data.
156b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      LOG(WARNING) << "Badly formed sync data in incoming notification";
157b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      continue;
158b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    }
1592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Process each incoming remote notification.
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& key = incoming->GetKey();
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DCHECK_GT(key.length(), 0U);
1637dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    SyncedNotification* found = FindNotificationById(key);
1642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (NULL == found) {
1662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // If there are no conflicts, copy in the data from remote.
1672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      Add(incoming.Pass());
1682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    } else {
1692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // If the incoming (remote) and stored (local) notifications match
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      // in all fields, we don't need to do anything here.
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      if (incoming->EqualsIgnoringReadState(*found)) {
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
173c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)        if (incoming->GetReadState() == found->GetReadState()) {
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // Notification matches on the client and the server, nothing to do.
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          continue;
1765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } else {
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // If the read state is different, read wins for both places.
178c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)          if (incoming->GetReadState() == SyncedNotification::kDismissed) {
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // If it is marked as read on the server, but not the client.
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            found->NotificationHasBeenDismissed();
181ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            // Tell the Notification UI Manager to remove it.
182ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            notification_manager_->CancelById(found->GetKey());
183ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          } else if (incoming->GetReadState() == SyncedNotification::kRead) {
184ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            // If it is marked as read on the server, but not the client.
185ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            found->NotificationHasBeenRead();
186ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            // Tell the Notification UI Manager to remove it.
1877dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch            notification_manager_->CancelById(found->GetKey());
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          } else {
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            // If it is marked as read on the client, but not the server.
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            syncer::SyncData sync_data = CreateSyncDataFromNotification(*found);
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)            new_changes.push_back(
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                syncer::SyncChange(FROM_HERE,
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   syncer::SyncChange::ACTION_UPDATE,
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                   sync_data));
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          }
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          // If local state changed, notify Notification UI Manager.
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        }
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      } else {
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // If different, just replace the local with the remote.
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // TODO(petewil): Someday we may allow changes from the client to
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        // flow upwards, when we do, we will need better merge resolution.
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        found->Update(sync_data);
2037dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
2047dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch        // Tell the notification manager to update the notification.
205ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        UpdateInMessageCenter(found);
2062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      }
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Send up the changes that were made locally.
2112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (new_changes.size() > 0) {
2122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    merge_result.set_error(sync_processor_->ProcessSyncChanges(
2132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        FROM_HERE, new_changes));
2142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2164e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Once we complete our first sync, we mark "first run" as false,
2174e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // subsequent runs of Synced Notifications will get normal treatment.
2184e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (synced_notification_first_run_) {
2194e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    synced_notification_first_run_ = false;
2204e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    profile_->GetPrefs()->SetBoolean(prefs::kSyncedNotificationFirstRun, false);
2214e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
2224e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
2232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return merge_result;
2242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ChromeNotifierService::StopSyncing(syncer::ModelType type) {
2272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
228d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Since this data type is not user-unselectable, we chose not to implement
229d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // the stop syncing method, and instead do nothing here.
2302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncDataList ChromeNotifierService::GetAllSyncData(
2335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    syncer::ModelType type) const {
2342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, type);
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncDataList sync_data;
2362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Copy our native format data into a SyncDataList format.
238ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ScopedVector<SyncedNotification>::const_iterator it =
239ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      notification_data_.begin();
240ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  for (; it != notification_data_.end(); ++it) {
2412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    sync_data.push_back(CreateSyncDataFromNotification(**it));
2422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
2432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return sync_data;
2452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
2462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This method is called when there is an incoming sync change from the server.
2482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncError ChromeNotifierService::ProcessSyncChanges(
2495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const tracked_objects::Location& from_here,
2505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncChangeList& change_list) {
251a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
2522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncError error;
2532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  for (syncer::SyncChangeList::const_iterator it = change_list.begin();
2552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       it != change_list.end(); ++it) {
2562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncData sync_data = it->sync_data();
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DCHECK_EQ(syncer::SYNCED_NOTIFICATIONS, sync_data.GetDataType());
2582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    syncer::SyncChange::SyncChangeType change_type = it->change_type();
2592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    scoped_ptr<SyncedNotification> new_notification(
2612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        CreateNotificationFromSyncData(sync_data));
2622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (!new_notification.get()) {
2632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      NOTREACHED() << "Failed to read notification.";
2642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      continue;
2652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
2662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
267ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const std::string& key = new_notification->GetKey();
268ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    DCHECK_GT(key.length(), 0U);
269ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    SyncedNotification* found = FindNotificationById(key);
270ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
2712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    switch (change_type) {
2722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      case syncer::SyncChange::ACTION_ADD:
273ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // Intentional fall through, cases are identical.
274ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      case syncer::SyncChange::ACTION_UPDATE:
275ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        if (found == NULL) {
276ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          Add(new_notification.Pass());
277ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          break;
278ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        }
279ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // Update it in our store.
280ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        found->Update(sync_data);
281ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // Tell the notification manager to update the notification.
282ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        UpdateInMessageCenter(found);
283ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        break;
284ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
285ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      case syncer::SyncChange::ACTION_DELETE:
286ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        if (found == NULL) {
287ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          break;
288ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        }
289ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // Remove it from our store.
290ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        FreeNotificationById(key);
291ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // Remove it from the message center.
292ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        UpdateInMessageCenter(new_notification.get());
293ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // TODO(petewil): Do I need to remember that it was deleted in case the
294ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        // add arrives after the delete?  If so, how long do I need to remember?
2952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
2962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
2972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      default:
298ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        NOTREACHED();
2992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)        break;
3002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
3012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return error;
3042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Support functions for data type conversion.
3072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Static method.  Get to the sync data in our internal format.
3092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)syncer::SyncData ChromeNotifierService::CreateSyncDataFromNotification(
3102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    const SyncedNotification& notification) {
3112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Construct the sync_data using the specifics from the notification.
3122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return syncer::SyncData::CreateLocalData(
313c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      notification.GetKey(), notification.GetKey(),
3142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      notification.GetEntitySpecifics());
3152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Static Method.  Convert from SyncData to our internal format.
3182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)scoped_ptr<SyncedNotification>
3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)ChromeNotifierService::CreateNotificationFromSyncData(
3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const syncer::SyncData& sync_data) {
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get a pointer to our data within the sync_data object.
3222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_pb::SyncedNotificationSpecifics specifics =
3232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      sync_data.GetSpecifics().synced_notification();
3242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Check for mandatory fields in the sync_data object.
3262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!specifics.has_coalesced_notification() ||
3272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      !specifics.coalesced_notification().has_key() ||
328b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      !specifics.coalesced_notification().has_read_state()) {
329b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    DVLOG(1) << "Synced Notification missing mandatory fields "
330b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             << "has coalesced notification? "
331b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             << specifics.has_coalesced_notification()
332b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             << " has key? " << specifics.coalesced_notification().has_key()
333b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             << " has read state? "
334b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             << specifics.coalesced_notification().has_read_state();
3352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return scoped_ptr<SyncedNotification>();
336b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
3372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_well_formed_unread_notification =
3392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (static_cast<SyncedNotification::ReadState>(
3402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          specifics.coalesced_notification().read_state()) ==
3412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       SyncedNotification::kUnread &&
3422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       specifics.coalesced_notification().has_render_info());
343ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  bool is_well_formed_read_notification =
344ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      (static_cast<SyncedNotification::ReadState>(
345ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch          specifics.coalesced_notification().read_state()) ==
346424c4d7b64af9d0d8fd9624f381f469654d5e3d2Torne (Richard Coles)       SyncedNotification::kRead);
3472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool is_well_formed_dismissed_notification =
3482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      (static_cast<SyncedNotification::ReadState>(
3492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)          specifics.coalesced_notification().read_state()) ==
3502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)       SyncedNotification::kDismissed);
3512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // If the notification is poorly formed, return a null pointer.
3532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!is_well_formed_unread_notification &&
354ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      !is_well_formed_read_notification &&
355b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      !is_well_formed_dismissed_notification) {
356b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    DVLOG(1) << "Synced Notification is not well formed."
357b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             << " unread well formed? "
358b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             << is_well_formed_unread_notification
359b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)             << " dismissed well formed? "
360ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch             << is_well_formed_dismissed_notification
361ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch             << " read well formed? "
362ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch             << is_well_formed_read_notification;
3632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return scoped_ptr<SyncedNotification>();
364b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  }
3652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Create a new notification object based on the supplied sync_data.
3672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  scoped_ptr<SyncedNotification> notification(
3682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      new SyncedNotification(sync_data));
3692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return notification.Pass();
3712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This returns a pointer into a vector that we own.  Caller must not free it.
3742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Returns NULL if no match is found.
3757dbb3d5cf0c15f500944d211057644d6a2f37371Ben MurdochSyncedNotification* ChromeNotifierService::FindNotificationById(
3767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    const std::string& notification_id) {
3772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(petewil): We can make a performance trade off here.
3782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // While the vector has good locality of reference, a map has faster lookup.
3792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Based on how big we expect this to get, maybe change this to a map.
380ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ScopedVector<SyncedNotification>::const_iterator it =
381ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      notification_data_.begin();
382ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  for (; it != notification_data_.end(); ++it) {
3832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    SyncedNotification* notification = *it;
3847dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (notification_id == notification->GetKey())
3852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return *it;
3862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
3872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
3882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return NULL;
3892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
3902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
391ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid ChromeNotifierService::FreeNotificationById(
392ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    const std::string& notification_id) {
393ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  ScopedVector<SyncedNotification>::iterator it = notification_data_.begin();
394ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  for (; it != notification_data_.end(); ++it) {
395ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    SyncedNotification* notification = *it;
396ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    if (notification_id == notification->GetKey()) {
397ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      notification_data_.erase(it);
398ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      return;
399ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    }
400ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
401ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
402ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
403eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid ChromeNotifierService::GetSyncedNotificationServices(
404eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    std::vector<message_center::Notifier*>* notifiers) {
4057dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // TODO(mukai|petewil): Check the profile's eligibility before adding the
4067dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  // sample app.
4075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ScopedVector<SyncedNotificationAppInfo>::iterator it = app_info_data_.begin();
4085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  for (; it != app_info_data_.end(); ++it) {
4095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncedNotificationAppInfo* app_info = *it;
4105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    message_center::NotifierId notifier_id = app_info->GetNotifierId();
4115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Enable or disable the sending service per saved settings.
4135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    bool app_enabled = false;
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    std::set<std::string>::iterator iter;
4155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    iter = find(enabled_sending_services_.begin(),
4165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                enabled_sending_services_.end(),
4175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                notifier_id.id);
4185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    app_enabled = iter != enabled_sending_services_.end();
4195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    message_center::Notifier* app_info_notifier = new message_center::Notifier(
4215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        notifier_id, app_info->service_name(), app_enabled);
4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    app_info_notifier->icon = app_info->small_icon();
4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // |notifiers| takes ownership of |app_info_notifier|.
4265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    notifiers->push_back(app_info_notifier);
4275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
428eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
429eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
430ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid ChromeNotifierService::MarkNotificationAsRead(
431c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    const std::string& key) {
432a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
4337dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  SyncedNotification* notification = FindNotificationById(key);
4342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK(notification != NULL);
4352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
436ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  notification->NotificationHasBeenRead();
4372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncChangeList new_changes;
4382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  syncer::SyncData sync_data = CreateSyncDataFromNotification(*notification);
4402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  new_changes.push_back(
4412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      syncer::SyncChange(FROM_HERE,
4422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         syncer::SyncChange::ACTION_UPDATE,
4432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                         sync_data));
4442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Send up the changes that were made locally.
4462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Add a new notification to our data structure.  This takes ownership
4502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// of the passed in pointer.
4512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)void ChromeNotifierService::Add(scoped_ptr<SyncedNotification> notification) {
4522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  SyncedNotification* notification_copy = notification.get();
4532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Take ownership of the object and put it into our local storage.
4542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  notification_data_.push_back(notification.release());
4552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
456a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // If the user is not interested in this type of notification, ignore it.
457d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  std::set<std::string>::iterator iter =
458a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch      find(enabled_sending_services_.begin(),
459a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch           enabled_sending_services_.end(),
460a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch           notification_copy->GetSendingServiceId());
461a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  if (iter == enabled_sending_services_.end()) {
4625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    iter = find(initialized_sending_services_.begin(),
4635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                initialized_sending_services_.end(),
4645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                notification_copy->GetSendingServiceId());
4655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (iter != initialized_sending_services_.end())
4665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return;
467a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  }
468a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
469ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  UpdateInMessageCenter(notification_copy);
4707dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch}
4717dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
4727dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdochvoid ChromeNotifierService::AddForTest(
4737dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    scoped_ptr<notifier::SyncedNotification> notification) {
474ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  notification_data_.push_back(notification.release());
475ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
4767dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
477ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid ChromeNotifierService::UpdateInMessageCenter(
478ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    SyncedNotification* notification) {
4792385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch  // If the feature is disabled, exit now.
4802385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch  if (!notifier::ChromeNotifierServiceFactory::UseSyncedNotifications(
481ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch      CommandLine::ForCurrentProcess()))
4822385ea399aae016c0806a4f9ef3c9cfe3d2a39dfBen Murdoch    return;
483a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
484ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  notification->LogNotification();
485ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
486ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  if (notification->GetReadState() == SyncedNotification::kUnread) {
487ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // If the message is unread, update it.
488ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    Display(notification);
489ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  } else {
490ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // If the message is read or deleted, dismiss it from the center.
491ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    // We intentionally ignore errors if it is not in the center.
492ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    notification_manager_->CancelById(notification->GetKey());
493ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch  }
494ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch}
495ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
496ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdochvoid ChromeNotifierService::Display(SyncedNotification* notification) {
4974e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // If this is the first run for the feature, don't surprise the user.
4984e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Instead, place all backlogged notifications into the notification
4994e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // center.
5004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  if (synced_notification_first_run_) {
5014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    // Setting the toast state to false will prevent toasting the notification.
5024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    notification->SetToastState(false);
5034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  }
5044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
5055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Our tests cannot use the network for reliability reasons.
5065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (avoid_bitmap_fetching_for_test_) {
5075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    notification->Show(notification_manager_, this, profile_);
5085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
5095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
5105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
5115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Set up to fetch the bitmaps.
5125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  notification->QueueBitmapFetchJobs(notification_manager_, this, profile_);
5135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
514868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // Start the bitmap fetching, Show() will be called when the last bitmap
515868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  // either arrives or times out.
5167dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  notification->StartBitmapFetch();
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
519eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid ChromeNotifierService::OnSyncedNotificationServiceEnabled(
520eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string& notifier_id, bool enabled) {
521d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  std::set<std::string>::iterator iter;
522d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
523d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Make a copy of the notifier_id, which might not have lifetime long enough
524d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // for this function to finish all of its work.
525d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  std::string notifier_id_copy(notifier_id);
526a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
527a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  iter = find(enabled_sending_services_.begin(),
528a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch              enabled_sending_services_.end(),
529d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              notifier_id_copy);
530d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
5314e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  base::ListValue synced_notification_services;
532a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
533a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  // Add the notifier_id if it is enabled and not already there.
534a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  if (iter == enabled_sending_services_.end() && enabled) {
535d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    enabled_sending_services_.insert(notifier_id_copy);
5363551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    // Check now for any outstanding notifications.
5373551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    DisplayUnreadNotificationsFromSource(notifier_id);
538d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    BuildServiceListValueInplace(enabled_sending_services_,
539d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                 &synced_notification_services);
540d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Add this preference to the enabled list.
5414e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    profile_->GetPrefs()->Set(prefs::kEnabledSyncedNotificationSendingServices,
5424e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                              synced_notification_services);
5435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    // Remove the notifier_id if it is disabled and present.
544a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  } else if (iter != enabled_sending_services_.end() && !enabled) {
545a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch    enabled_sending_services_.erase(iter);
546d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    BuildServiceListValueInplace(enabled_sending_services_,
547d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                                 &synced_notification_services);
548d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    // Remove this peference from the enabled list.
5494e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    profile_->GetPrefs()->Set(prefs::kEnabledSyncedNotificationSendingServices,
5504e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                              synced_notification_services);
551d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    RemoveUnreadNotificationsFromSource(notifier_id_copy);
552a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  }
553a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch
5548bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Collect UMA statistics when a service is enabled or disabled.
5558bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (enabled) {
5568bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    content::RecordAction(
5578bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        UserMetricsAction("SyncedNotifications.SendingServiceEnabled"));
5588bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  } else {
5598bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    content::RecordAction(
5608bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        UserMetricsAction("SyncedNotifications.SendingServiceDisabled"));
5618bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
5628bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
5638bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Collect individual service enabling/disabling statistics.
5648bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  CollectPerServiceEnablingStatistics(notifier_id, enabled);
5658bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
566a3f7b4e666c476898878fa745f637129375cd889Ben Murdoch  return;
567eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
568eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
5698bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void ChromeNotifierService::CollectPerServiceEnablingStatistics(
5708bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    const std::string& notifier_id,
5718bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    bool enabled) {
5728bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // TODO(petewil) - This approach does not scale well as we add new services,
5738bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // but we are limited to using predefined ENUM values in histogram based UMA
5748bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // data, which does not permit arbitrary strings.
5758bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Find a way to make it scale, or remove enum value this when we have enough
5768bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // data.
5778bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
5788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  ChromeNotifierServiceActionType action =
5798bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      CHROME_NOTIFIER_SERVICE_ACTION_UNKNOWN;
5808bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
5818bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // Derive action type from notifier_id and enabled.
5828bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  // TODO(petewil): Add more sending services as they are enabled.
5838bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  if (notifier_id == std::string(kFirstSyncedNotificationServiceId)) {
5848bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    action = enabled
5858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        ? CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_ENABLED
5868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)        : CHROME_NOTIFIER_SERVICE_ACTION_FIRST_SERVICE_DISABLED;
5878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  }
5888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
5898bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION("ChromeNotifierService.Actions",
5908bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                            action,
5918bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                            CHROME_NOTIFIER_SERVICE_ACTION_COUNT);
5928bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
5938bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
594d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void ChromeNotifierService::BuildServiceListValueInplace(
5954e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    std::set<std::string> services, base::ListValue* list_value) {
596d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  std::set<std::string>::iterator iter;
597d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
598d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Iterate over the strings, adding each one to the list value
599d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  for (iter = services.begin();
600d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)       iter != services.end();
601d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)       ++iter) {
6024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    base::StringValue* string_value(new base::StringValue(*iter));
6034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    list_value->Append(string_value);
604d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
605d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
606d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
6073551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)void ChromeNotifierService::DisplayUnreadNotificationsFromSource(
608d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const std::string& notifier_id) {
6093551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  for (std::vector<SyncedNotification*>::const_iterator iter =
6103551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          notification_data_.begin();
6113551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)       iter != notification_data_.end();
6123551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)       ++iter) {
6133551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if ((*iter)->GetSendingServiceId() == notifier_id &&
6143551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)        (*iter)->GetReadState() == SyncedNotification::kUnread)
6153551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      Display(*iter);
6163551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
6173551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
6183551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
6193551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)void ChromeNotifierService::RemoveUnreadNotificationsFromSource(
620d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    const std::string& notifier_id) {
6213551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  for (std::vector<SyncedNotification*>::const_iterator iter =
6223551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)          notification_data_.begin();
6233551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)       iter != notification_data_.end();
6243551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)       ++iter) {
6253551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    if ((*iter)->GetSendingServiceId() == notifier_id &&
626d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)        (*iter)->GetReadState() == SyncedNotification::kUnread) {
6273551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      notification_manager_->CancelById((*iter)->GetKey());
628d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    }
6293551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
6303551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
6313551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
632d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void ChromeNotifierService::OnEnabledSendingServiceListPrefChanged(
633d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    std::set<std::string>* ids_field) {
634d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  ids_field->clear();
635d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const std::vector<std::string> pref_list =
636d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      enabled_sending_services_prefs_.GetValue();
637d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  for (size_t i = 0; i < pref_list.size(); ++i) {
638d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    std::string element = pref_list[i];
639d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!element.empty())
640d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      ids_field->insert(element);
641d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    else
642d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      LOG(WARNING) << i << "-th element is not a string "
643d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                   << prefs::kEnabledSyncedNotificationSendingServices;
644d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
645d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
646d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
647d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void ChromeNotifierService::OnInitializedSendingServiceListPrefChanged(
648d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    std::set<std::string>* ids_field) {
649d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  ids_field->clear();
650d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  const std::vector<std::string> pref_list =
651d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      initialized_sending_services_prefs_.GetValue();
652d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  for (size_t i = 0; i < pref_list.size(); ++i) {
653d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    std::string element = pref_list[i];
654d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    if (!element.empty())
655d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      ids_field->insert(element);
656d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    else
657d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      LOG(WARNING) << i << "-th element is not a string for "
658d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)                   << prefs::kInitializedSyncedNotificationSendingServices;
659d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  }
660d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
661d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
6624e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)void ChromeNotifierService::OnSyncedNotificationFirstRunBooleanPrefChanged(
6634e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)    bool* new_value) {
6644e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  synced_notification_first_run_ = *new_value;
6654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)}
6664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)
667d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void ChromeNotifierService::RegisterProfilePrefs(
668d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    user_prefs::PrefRegistrySyncable* registry) {
669d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Register the pref for the list of enabled services.
670d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  registry->RegisterListPref(
671d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      prefs::kEnabledSyncedNotificationSendingServices,
6725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
673d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Register the pref for the list of initialized services.
674d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  registry->RegisterListPref(
675d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      prefs::kInitializedSyncedNotificationSendingServices,
6765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
6774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // Register the preference for first run status, defaults to "true",
6784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  // meaning that this is the first run of the Synced Notification feature.
6794e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  registry->RegisterBooleanPref(
6804e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      prefs::kSyncedNotificationFirstRun, true,
6814e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
682d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
683d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
684d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)void ChromeNotifierService::InitializePrefs() {
685d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Set up any pref changes to update our list of services.
686d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  enabled_sending_services_prefs_.Init(
687d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      prefs::kEnabledSyncedNotificationSendingServices,
688d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      profile_->GetPrefs(),
689d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      base::Bind(
690d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          &ChromeNotifierService::OnEnabledSendingServiceListPrefChanged,
691d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          base::Unretained(this),
692d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          base::Unretained(&enabled_sending_services_)));
693d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  initialized_sending_services_prefs_.Init(
694d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      prefs::kInitializedSyncedNotificationSendingServices,
695d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      profile_->GetPrefs(),
696d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)      base::Bind(
697d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          &ChromeNotifierService::OnInitializedSendingServiceListPrefChanged,
698d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          base::Unretained(this),
699d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)          base::Unretained(&initialized_sending_services_)));
7004e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  synced_notification_first_run_prefs_.Init(
7014e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      prefs::kSyncedNotificationFirstRun,
7024e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      profile_->GetPrefs(),
7034e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      base::Bind(
7044e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          &ChromeNotifierService::
7054e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)              OnSyncedNotificationFirstRunBooleanPrefChanged,
7064e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          base::Unretained(this),
7074e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)          base::Unretained(&synced_notification_first_run_)));
708d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
709d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Get the prefs from last session into our memeber varilables
710d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  OnEnabledSendingServiceListPrefChanged(&enabled_sending_services_);
711d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  OnInitializedSendingServiceListPrefChanged(&initialized_sending_services_);
7128bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
7134e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  synced_notification_first_run_ =
7144e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      profile_->GetPrefs()->GetBoolean(prefs::kSyncedNotificationFirstRun);
715d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
716d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
7175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)void ChromeNotifierService::ShowWelcomeToastIfNecessary(
7185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const SyncedNotification* synced_notification,
7195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    NotificationUIManager* notification_ui_manager) {
7205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  const std::string& sending_service_id =
7215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      synced_notification->GetSendingServiceId();
722d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
723d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  std::set<std::string>::iterator iter;
724d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  iter = find(initialized_sending_services_.begin(),
725d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)              initialized_sending_services_.end(),
7265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)              sending_service_id);
7275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // If we already initialized the sending service, then return early since no
7295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // welcome toast is necessary.
7305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (iter != initialized_sending_services_.end())
731d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)    return;
732d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
7335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // If there is no app info, we can't show a welcome toast.  Ideally all synced
7345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // notifications will be delayed until an app_info data structure can be
7355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // constructed for them.
7365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // TODO(dewittj): Refactor when app_info is populated asynchronously.
7375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  SyncedNotificationAppInfo* app_info = FindAppInfo(sending_service_id);
7385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (!app_info)
7395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    return;
7405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // TODO(dewittj): Ensure that the app info icon is set before this point.
7425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (app_info->icon().IsEmpty()) {
7435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    gfx::Image notification_app_icon = synced_notification->GetAppIcon();
7445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if (!notification_app_icon.IsEmpty()) {
7455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      app_info->set_icon(notification_app_icon);
7465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    } else {
7475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // This block should only be reached in tests since the downloads are
7485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      // already finished for |synced_notification|.
7495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      DVLOG(1) << "Unable to find the app icon for the welcome notification. "
7505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)               << "Service ID: " << sending_service_id;
7515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
7525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7535d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  message_center::NotifierId notifier_id(
7555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      message_center::NotifierId::SYNCED_NOTIFICATION_SERVICE,
7565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      sending_service_id);
7575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  Notification notification = CreateWelcomeNotificationForService(app_info);
7595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  notification_ui_manager->Add(notification, profile_);
7605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  enabled_sending_services_.insert(sending_service_id);
7625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  initialized_sending_services_.insert(sending_service_id);
7635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
764d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Build a ListValue with the list of services to be enabled.
7654e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  base::ListValue enabled_sending_services;
7664e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  base::ListValue initialized_sending_services;
767d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)
768d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)  // Mark any new services as enabled in preferences.
7694e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  BuildServiceListValueInplace(enabled_sending_services_,
7704e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                               &enabled_sending_services);
7715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  profile_->GetPrefs()->Set(prefs::kEnabledSyncedNotificationSendingServices,
7725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                            enabled_sending_services);
7735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // Mark any new services as initialized in preferences.
7744e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  BuildServiceListValueInplace(initialized_sending_services_,
7754e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)                               &initialized_sending_services);
7764e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)  profile_->GetPrefs()->Set(
7774e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      prefs::kInitializedSyncedNotificationSendingServices,
7784e180b6a0b4720a9b8e9e959a882386f690f08ffTorne (Richard Coles)      initialized_sending_services);
779d0247b1b59f9c528cb6df88b4f2b9afaf80d181eTorne (Richard Coles)}
7803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
7815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)SyncedNotificationAppInfo* ChromeNotifierService::FindAppInfo(
7825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    const std::string& app_id) const {
7835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  ScopedVector<SyncedNotificationAppInfo>::const_iterator iter =
7845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      app_info_data_.begin();
7855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  while (iter != app_info_data_.end()) {
7865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    if ((*iter)->app_id() == app_id)
7875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      return (*iter);
7885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    ++iter;
7905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  }
7915d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return NULL;
7935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
7945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
7955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)const Notification ChromeNotifierService::CreateWelcomeNotificationForService(
7965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    SyncedNotificationAppInfo* app_info) {
7975d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  std::string welcome_notification_id = base::GenerateGUID();
7985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  scoped_refptr<WelcomeDelegate> delegate(new WelcomeDelegate(
7995d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      welcome_notification_id, profile_, app_info->GetNotifierId()));
8005d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  message_center::ButtonInfo button_info(
8025d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      l10n_util::GetStringUTF16(IDS_NOTIFIER_WELCOME_BUTTON));
8035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  button_info.icon = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
8045d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      IDR_NOTIFIER_BLOCK_BUTTON);
8055d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  message_center::RichNotificationData rich_notification_data;
8075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  rich_notification_data.buttons.push_back(button_info);
8085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  return Notification(
8095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      message_center::NOTIFICATION_TYPE_BASE_FORMAT,
8105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      GURL(kSyncedNotificationsWelcomeOrigin),
8115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      app_info->title(),
8125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      l10n_util::GetStringUTF16(IDS_NOTIFIER_WELCOME_BODY),
8135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      app_info->icon(),
8145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      blink::WebTextDirectionDefault,
8155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      app_info->GetNotifierId(),
8165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_DISPLAY_SOURCE),
8175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::UTF8ToUTF16(welcome_notification_id),
8185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      rich_notification_data,
8195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      delegate.get());
8205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)}
8215d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
8222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}  // namespace notifier
823