| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // The SyncedNotificationAppInfoService brings down read only metadata from the |
| // sync server with information about the services sending synced notifications. |
| |
| #include "chrome/browser/notifications/sync_notifier/synced_notification_app_info_service.h" |
| |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h" |
| #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "sync/api/sync_change.h" |
| #include "sync/api/sync_change_processor.h" |
| #include "sync/api/sync_error_factory.h" |
| #include "sync/protocol/sync.pb.h" |
| #include "sync/protocol/synced_notification_app_info_specifics.pb.h" |
| #include "url/gurl.h" |
| |
| namespace notifier { |
| |
| SyncedNotificationSendingServiceSettingsData:: |
| SyncedNotificationSendingServiceSettingsData( |
| std::string settings_display_name_param, |
| gfx::Image settings_icon_param, |
| message_center::NotifierId notifier_id_param) |
| : settings_display_name(settings_display_name_param), |
| settings_icon(settings_icon_param), |
| notifier_id(notifier_id_param) {} |
| |
| bool SyncedNotificationAppInfoService::avoid_bitmap_fetching_for_test_ = false; |
| |
| SyncedNotificationAppInfoService::SyncedNotificationAppInfoService( |
| Profile* profile) |
| : profile_(profile), chrome_notifier_service_(NULL) {} |
| |
| SyncedNotificationAppInfoService::~SyncedNotificationAppInfoService() {} |
| |
| // Methods from KeyedService. |
| void SyncedNotificationAppInfoService::Shutdown() {} |
| |
| // syncer::SyncableService implementation. |
| |
| // This is called at startup to sync with the sync data. This code is not thread |
| // safe, it should only be called by sync on the browser thread. |
| syncer::SyncMergeResult |
| SyncedNotificationAppInfoService::MergeDataAndStartSyncing( |
| syncer::ModelType type, |
| const syncer::SyncDataList& initial_sync_data, |
| scoped_ptr<syncer::SyncChangeProcessor> sync_processor, |
| scoped_ptr<syncer::SyncErrorFactory> error_handler) { |
| thread_checker_.CalledOnValidThread(); |
| DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type); |
| syncer::SyncMergeResult merge_result(syncer::SYNCED_NOTIFICATION_APP_INFO); |
| |
| // There should only be one sync data in the list for this data type. |
| if (initial_sync_data.size() > 1) { |
| LOG(ERROR) << "Too many app infos over sync"; |
| } |
| |
| // TODO(petewil): Today we can only handle a single object, so we simply check |
| // for a non-empty list. If in the future we can ever handle more, convert |
| // this whole block to be a loop over all of |initial_sync_data|. |
| if (!initial_sync_data.empty()) { |
| const syncer::SyncData& sync_data = initial_sync_data.front(); |
| DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, sync_data.GetDataType()); |
| |
| const sync_pb::SyncedNotificationAppInfoSpecifics& specifics = |
| sync_data.GetSpecifics().synced_notification_app_info(); |
| |
| // Store our sync data, so GetAllSyncData can give it back later. |
| sync_data_ = sync_data; |
| |
| size_t app_info_count = specifics.synced_notification_app_info_size(); |
| |
| // The SyncedNotificationAppInfo is a repeated field, process each one. |
| for (size_t app_info_index = 0; |
| app_info_index < app_info_count; |
| ++app_info_index) { |
| const sync_pb::SyncedNotificationAppInfo app_info( |
| specifics.synced_notification_app_info(app_info_index)); |
| |
| ProcessIncomingAppInfoProtobuf(app_info); |
| } |
| } |
| |
| return merge_result; |
| } |
| |
| void SyncedNotificationAppInfoService::StopSyncing(syncer::ModelType type) { |
| DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type); |
| // Implementation is not required, since this is not a user selectable sync |
| // type. |
| } |
| |
| // This method is called when there is an incoming sync change from the server. |
| syncer::SyncError SyncedNotificationAppInfoService::ProcessSyncChanges( |
| const tracked_objects::Location& from_here, |
| const syncer::SyncChangeList& change_list) { |
| thread_checker_.CalledOnValidThread(); |
| syncer::SyncError error; |
| |
| for (syncer::SyncChangeList::const_iterator it = change_list.begin(); |
| it != change_list.end(); |
| ++it) { |
| syncer::SyncData sync_data = it->sync_data(); |
| DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, sync_data.GetDataType()); |
| syncer::SyncChange::SyncChangeType change_type = it->change_type(); |
| DCHECK(change_type == syncer::SyncChange::ACTION_UPDATE || |
| change_type == syncer::SyncChange::ACTION_ADD); |
| |
| sync_pb::SyncedNotificationAppInfoSpecifics specifics = |
| sync_data.GetSpecifics().synced_notification_app_info(); |
| |
| // Copy over the sync data with the new one. |
| sync_data_ = sync_data; |
| |
| size_t app_info_count = specifics.synced_notification_app_info_size(); |
| if (app_info_count == 0) { |
| NOTREACHED() << "Bad notification app info change from the server."; |
| continue; |
| } |
| |
| // The SyncedNotificationAppInfo is a repeated field, process each one. |
| for (size_t app_info_index = 0; |
| app_info_index < app_info_count; |
| ++app_info_index) { |
| const sync_pb::SyncedNotificationAppInfo app_info( |
| specifics.synced_notification_app_info(app_info_index)); |
| ProcessIncomingAppInfoProtobuf(app_info); |
| } |
| } |
| |
| return error; |
| } |
| |
| syncer::SyncDataList SyncedNotificationAppInfoService::GetAllSyncData( |
| syncer::ModelType type) const { |
| DCHECK_EQ(syncer::SYNCED_NOTIFICATION_APP_INFO, type); |
| syncer::SyncDataList sync_data_list; |
| |
| // TODO(petewil): Recreate this from internal data instead, |
| // it improves code maintainability. |
| |
| // make a copy for the list we give back to the caller. |
| sync_data_list.push_back(sync_data_); |
| |
| return sync_data_list; |
| } |
| |
| // When a new protobuf comes in, we will fetch all the bitmaps before reporting |
| // the new protobufs to the ChromeNotifierService. This helps insure that new |
| // protobufs appear at the same time old ones disappear, and that no protobuf |
| // appears before its bitmaps are present. |
| // We also check for duplicate bitmap URLs when loading the bitmaps for a |
| // protobuf, so we aren't left waiting for a second copy of the bitmap to be |
| // fetched before we call the protobuf done. |
| void SyncedNotificationAppInfoService::ProcessIncomingAppInfoProtobuf( |
| const sync_pb::SyncedNotificationAppInfo& app_info) { |
| // Build a local app_info object from the sync data. |
| scoped_ptr<SyncedNotificationAppInfo> incoming( |
| CreateSyncedNotificationAppInfoFromProtobuf(app_info)); |
| if (incoming.get() == NULL) { |
| LOG(ERROR) << "Invalid Synced No5tification App Info protobuf"; |
| return; |
| } |
| |
| // Process each incoming app_info protobuf. |
| std::string app_info_name = incoming->settings_display_name(); |
| DCHECK_GT(app_info_name.length(), 0U); |
| if (app_info_name.length() == 0) { |
| // If there is no unique id (name), there is nothing we can do. |
| return; |
| } |
| |
| SyncedNotificationAppInfo* found = |
| FindSyncedNotificationAppInfoByName(app_info_name); |
| |
| std::vector<std::string> old_app_ids; |
| std::vector<std::string> new_app_ids; |
| std::vector<std::string> added_app_ids; |
| std::vector<std::string> removed_app_ids; |
| |
| new_app_ids = incoming->GetAppIdList(); |
| |
| if (NULL == found) { |
| added_app_ids = new_app_ids; |
| } else { |
| // When we have an update, some app id types may be added or removed. |
| // Append to lists of added and removed types. |
| old_app_ids = found->GetAppIdList(); |
| new_app_ids = incoming->GetAppIdList(); |
| FreeSyncedNotificationAppInfoByName(app_info_name); |
| |
| // Set up for a set difference by sorting the lists. |
| std::sort(old_app_ids.begin(), old_app_ids.end()); |
| std::sort(new_app_ids.begin(), new_app_ids.end()); |
| |
| // Calculate which app ids are removed (in old, but not in new app ids). |
| std::set_difference(old_app_ids.begin(), |
| old_app_ids.end(), |
| new_app_ids.begin(), |
| new_app_ids.end(), |
| std::back_inserter(removed_app_ids)); |
| |
| // Calculate which app_ids are added (in new, but not in old app ids). |
| std::set_difference(new_app_ids.begin(), |
| new_app_ids.end(), |
| old_app_ids.begin(), |
| old_app_ids.end(), |
| std::back_inserter(added_app_ids)); |
| } |
| |
| // Put these lists into the app_info object. |
| incoming->set_added_app_ids(added_app_ids); |
| incoming->set_removed_app_ids(removed_app_ids); |
| |
| // Start bitmap fetch. |
| if (!avoid_bitmap_fetching_for_test_) { |
| incoming->QueueBitmapFetchJobs(); |
| incoming->StartBitmapFetch(); |
| } else { |
| OnBitmapFetchesDone(incoming->added_app_ids(), incoming->removed_app_ids()); |
| } |
| |
| sending_service_infos_.push_back(incoming.release()); |
| } |
| |
| void SyncedNotificationAppInfoService::OnBitmapFetchesDone( |
| std::vector<std::string> added_app_ids, |
| std::vector<std::string> removed_app_ids) { |
| // Tell the Chrome Notifier Service so it can show any notifications that were |
| // waiting for the app id to arrive, and to remave any notifications that are |
| // no longer supported. |
| if (chrome_notifier_service_ != NULL) { |
| chrome_notifier_service_->OnAddedAppIds(added_app_ids); |
| chrome_notifier_service_->OnRemovedAppIds(removed_app_ids); |
| } |
| } |
| |
| // Static Method. Convert from a server protobuf to our internal format. |
| scoped_ptr<SyncedNotificationAppInfo> |
| SyncedNotificationAppInfoService::CreateSyncedNotificationAppInfoFromProtobuf( |
| const sync_pb::SyncedNotificationAppInfo& server_app_info) { |
| |
| // Check for mandatory fields in the sync_data object. |
| std::string display_name; |
| if (server_app_info.has_app_name()) { |
| display_name = server_app_info.app_name(); |
| } else if (server_app_info.has_settings_display_name()) { |
| display_name = server_app_info.settings_display_name(); |
| } |
| |
| scoped_ptr<SyncedNotificationAppInfo> app_info; |
| if (display_name.length() == 0) |
| return app_info.Pass(); |
| |
| // Create a new app info object based on the supplied protobuf. |
| app_info.reset(new SyncedNotificationAppInfo(profile_, display_name, this)); |
| |
| // This URL is used whenever the user clicks on the body of the app's welcome |
| // notification. |
| if (server_app_info.has_info_url()) { |
| app_info->SetWelcomeLinkUrl(GURL(server_app_info.info_url())); |
| } |
| |
| // TODO(petewil): Eventually we will add the monochrome icon here, and we may |
| // need to fetch the correct url for the current DPI. |
| // Add the icon URL, if any. |
| if (server_app_info.has_icon()) { |
| std::string icon_url = server_app_info.icon().url(); |
| // Set the URLs for the low and high DPI images. |
| // TODO(petewil): Since the high DPI image is not available yet, we just |
| // pass an empty URL for now. Fix this once the high DPI URL is available. |
| app_info->SetSettingsURLs(GURL(icon_url), GURL()); |
| } |
| |
| // Add all the AppIds from the protobuf. |
| size_t app_id_count = server_app_info.app_id_size(); |
| for (size_t ii = 0; ii < app_id_count; ++ii) { |
| app_info->AddAppId(server_app_info.app_id(ii)); |
| } |
| |
| return app_info.Pass(); |
| } |
| |
| // This returns a pointer into a vector that we own. Caller must not free it. |
| // Returns NULL if no match is found. |
| notifier::SyncedNotificationAppInfo* |
| SyncedNotificationAppInfoService::FindSyncedNotificationAppInfoByName( |
| const std::string& name) { |
| for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it = |
| sending_service_infos_.begin(); |
| it != sending_service_infos_.end(); |
| ++it) { |
| SyncedNotificationAppInfo* app_info = *it; |
| if (name == app_info->settings_display_name()) |
| return *it; |
| } |
| |
| return NULL; |
| } |
| |
| // This returns a pointer into a vector that we own. Caller must not free it. |
| // Returns NULL if no match is found. |
| notifier::SyncedNotificationAppInfo* |
| SyncedNotificationAppInfoService::FindSyncedNotificationAppInfoByAppId( |
| const std::string& app_id) { |
| for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it = |
| sending_service_infos_.begin(); |
| it != sending_service_infos_.end(); |
| ++it) { |
| SyncedNotificationAppInfo* app_info = *it; |
| if (app_info->HasAppId(app_id)) |
| return *it; |
| } |
| |
| return NULL; |
| } |
| |
| // Lookup the sending service name for a given app id. |
| std::string SyncedNotificationAppInfoService::FindSendingServiceNameFromAppId( |
| const std::string app_id) { |
| for (ScopedVector<SyncedNotificationAppInfo>::const_iterator it = |
| sending_service_infos_.begin(); |
| it != sending_service_infos_.end(); |
| ++it) { |
| SyncedNotificationAppInfo* app_info = *it; |
| if (app_info->HasAppId(app_id)) |
| return app_info->settings_display_name(); |
| } |
| |
| return std::string(); |
| } |
| |
| void SyncedNotificationAppInfoService::FreeSyncedNotificationAppInfoByName( |
| const std::string& name) { |
| ScopedVector<SyncedNotificationAppInfo>::iterator it = |
| sending_service_infos_.begin(); |
| for (; it != sending_service_infos_.end(); ++it) { |
| SyncedNotificationAppInfo* app_info = *it; |
| if (name == app_info->settings_display_name()) { |
| sending_service_infos_.erase(it); |
| return; |
| } |
| } |
| } |
| |
| std::vector<SyncedNotificationSendingServiceSettingsData> |
| SyncedNotificationAppInfoService::GetAllSendingServiceSettingsData() { |
| std::vector<SyncedNotificationSendingServiceSettingsData> settings_data; |
| ScopedVector<SyncedNotificationAppInfo>::iterator it = |
| sending_service_infos_.begin(); |
| |
| for (; it != sending_service_infos_.end(); ++it) { |
| SyncedNotificationSendingServiceSettingsData this_service( |
| (*it)->settings_display_name(), |
| (*it)->icon(), |
| (*it)->GetNotifierId()); |
| settings_data.push_back(this_service); |
| } |
| |
| return settings_data; |
| } |
| |
| // Add a new app info to our data structure. This takes ownership |
| // of the passed in pointer. |
| void SyncedNotificationAppInfoService::Add( |
| scoped_ptr<SyncedNotificationAppInfo> app_info) { |
| sending_service_infos_.push_back(app_info.release()); |
| } |
| |
| } // namespace notifier |