| OLD | NEW |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "components/ntp_snippets/content_suggestions_service.h" | 5 #include "components/ntp_snippets/content_suggestions_service.h" |
| 6 | 6 |
| 7 #include <algorithm> | 7 #include <algorithm> |
| 8 #include <iterator> | 8 #include <iterator> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| 11 #include "base/strings/string_number_conversions.h" | 11 #include "base/strings/string_number_conversions.h" |
| 12 #include "ui/gfx/image/image.h" | 12 #include "ui/gfx/image/image.h" |
| 13 | 13 |
| 14 namespace ntp_snippets { | 14 namespace ntp_snippets { |
| 15 | 15 |
| 16 bool ContentSuggestionsService::CompareCategoriesByID::operator()( |
| 17 const ContentSuggestionsCategory& left, |
| 18 const ContentSuggestionsCategory& right) const { |
| 19 return left.id() < right.id(); |
| 20 } |
| 21 |
| 16 ContentSuggestionsService::ContentSuggestionsService(State state) | 22 ContentSuggestionsService::ContentSuggestionsService(State state) |
| 17 : state_(state) {} | 23 : state_(state) {} |
| 18 | 24 |
| 19 ContentSuggestionsService::~ContentSuggestionsService() {} | 25 ContentSuggestionsService::~ContentSuggestionsService() {} |
| 20 | 26 |
| 21 void ContentSuggestionsService::Shutdown() { | 27 void ContentSuggestionsService::Shutdown() { |
| 22 DCHECK(providers_.empty()); | 28 DCHECK(providers_.empty()); |
| 23 DCHECK(categories_.empty()); | 29 DCHECK(categories_.empty()); |
| 24 DCHECK(suggestions_by_category_.empty()); | 30 DCHECK(suggestions_by_category_.empty()); |
| 25 DCHECK(id_category_map_.empty()); | 31 DCHECK(id_category_map_.empty()); |
| (...skipping 25 matching lines...) Expand all Loading... |
| 51 } | 57 } |
| 52 | 58 |
| 53 void ContentSuggestionsService::FetchSuggestionImage( | 59 void ContentSuggestionsService::FetchSuggestionImage( |
| 54 const std::string& suggestion_id, | 60 const std::string& suggestion_id, |
| 55 const ImageFetchedCallback& callback) { | 61 const ImageFetchedCallback& callback) { |
| 56 if (!id_category_map_.count(suggestion_id)) { | 62 if (!id_category_map_.count(suggestion_id)) { |
| 57 LOG(WARNING) << "Requested image for unknown suggestion " << suggestion_id; | 63 LOG(WARNING) << "Requested image for unknown suggestion " << suggestion_id; |
| 58 callback.Run(suggestion_id, gfx::Image()); | 64 callback.Run(suggestion_id, gfx::Image()); |
| 59 return; | 65 return; |
| 60 } | 66 } |
| 61 ContentSuggestionsCategory category = id_category_map_[suggestion_id]; | 67 ContentSuggestionsCategory category = id_category_map_.at(suggestion_id); |
| 62 if (!providers_.count(category)) { | 68 if (!providers_.count(category)) { |
| 63 LOG(WARNING) << "Requested image for suggestion " << suggestion_id | 69 LOG(WARNING) << "Requested image for suggestion " << suggestion_id |
| 64 << " for unavailable category " << static_cast<int>(category); | 70 << " for unavailable category " << category; |
| 65 callback.Run(suggestion_id, gfx::Image()); | 71 callback.Run(suggestion_id, gfx::Image()); |
| 66 return; | 72 return; |
| 67 } | 73 } |
| 68 providers_[category]->FetchSuggestionImage(suggestion_id, callback); | 74 providers_[category]->FetchSuggestionImage(suggestion_id, callback); |
| 69 } | 75 } |
| 70 | 76 |
| 71 void ContentSuggestionsService::ClearCachedSuggestionsForDebugging() { | 77 void ContentSuggestionsService::ClearCachedSuggestionsForDebugging() { |
| 72 suggestions_by_category_.clear(); | 78 suggestions_by_category_.clear(); |
| 73 id_category_map_.clear(); | 79 id_category_map_.clear(); |
| 74 for (auto& category_provider_pair : providers_) { | 80 for (auto& category_provider_pair : providers_) { |
| 75 category_provider_pair.second->ClearCachedSuggestionsForDebugging(); | 81 category_provider_pair.second->ClearCachedSuggestionsForDebugging(); |
| 76 } | 82 } |
| 77 FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions()); | 83 FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions()); |
| 78 } | 84 } |
| 79 | 85 |
| 80 void ContentSuggestionsService::ClearDismissedSuggestionsForDebugging() { | 86 void ContentSuggestionsService::ClearDismissedSuggestionsForDebugging() { |
| 81 for (auto& category_provider_pair : providers_) { | 87 for (auto& category_provider_pair : providers_) { |
| 82 category_provider_pair.second->ClearDismissedSuggestionsForDebugging(); | 88 category_provider_pair.second->ClearDismissedSuggestionsForDebugging(); |
| 83 } | 89 } |
| 84 } | 90 } |
| 85 | 91 |
| 86 void ContentSuggestionsService::DismissSuggestion( | 92 void ContentSuggestionsService::DismissSuggestion( |
| 87 const std::string& suggestion_id) { | 93 const std::string& suggestion_id) { |
| 88 if (!id_category_map_.count(suggestion_id)) { | 94 if (!id_category_map_.count(suggestion_id)) { |
| 89 LOG(WARNING) << "Dismissed unknown suggestion " << suggestion_id; | 95 LOG(WARNING) << "Dismissed unknown suggestion " << suggestion_id; |
| 90 return; | 96 return; |
| 91 } | 97 } |
| 92 ContentSuggestionsCategory category = id_category_map_[suggestion_id]; | 98 ContentSuggestionsCategory category = id_category_map_.at(suggestion_id); |
| 93 if (!providers_.count(category)) { | 99 if (!providers_.count(category)) { |
| 94 LOG(WARNING) << "Dismissed suggestion " << suggestion_id | 100 LOG(WARNING) << "Dismissed suggestion " << suggestion_id |
| 95 << " for unavailable category " << static_cast<int>(category); | 101 << " for unavailable category " << category; |
| 96 return; | 102 return; |
| 97 } | 103 } |
| 98 providers_[category]->DismissSuggestion(suggestion_id); | 104 providers_[category]->DismissSuggestion(suggestion_id); |
| 99 | 105 |
| 100 // Remove the suggestion locally. | 106 // Remove the suggestion locally. |
| 101 id_category_map_.erase(suggestion_id); | 107 id_category_map_.erase(suggestion_id); |
| 102 std::vector<ContentSuggestion>* suggestions = | 108 std::vector<ContentSuggestion>* suggestions = |
| 103 &suggestions_by_category_[category]; | 109 &suggestions_by_category_[category]; |
| 104 auto position = | 110 auto position = |
| 105 std::find_if(suggestions->begin(), suggestions->end(), | 111 std::find_if(suggestions->begin(), suggestions->end(), |
| (...skipping 15 matching lines...) Expand all Loading... |
| 121 observers_.RemoveObserver(observer); | 127 observers_.RemoveObserver(observer); |
| 122 } | 128 } |
| 123 | 129 |
| 124 void ContentSuggestionsService::RegisterProvider( | 130 void ContentSuggestionsService::RegisterProvider( |
| 125 ContentSuggestionsProvider* provider) { | 131 ContentSuggestionsProvider* provider) { |
| 126 // TODO(pke): When NTPSnippetsService is purely a provider, think about | 132 // TODO(pke): When NTPSnippetsService is purely a provider, think about |
| 127 // removing this state check. | 133 // removing this state check. |
| 128 if (state_ == State::DISABLED) | 134 if (state_ == State::DISABLED) |
| 129 return; | 135 return; |
| 130 | 136 |
| 131 for (ContentSuggestionsCategory category : provider->provided_categories()) { | 137 for (ContentSuggestionsCategory category : |
| 138 provider->GetProvidedCategories()) { |
| 132 DCHECK_EQ(0ul, providers_.count(category)); | 139 DCHECK_EQ(0ul, providers_.count(category)); |
| 133 providers_[category] = provider; | 140 providers_[category] = provider; |
| 134 // TODO(pke): In the future, make sure that the categories have some useful | |
| 135 // (maybe constant, at least consistent) ordering for the UI. | |
| 136 categories_.push_back(category); | 141 categories_.push_back(category); |
| 137 if (IsContentSuggestionsCategoryStatusAvailable( | 142 if (IsContentSuggestionsCategoryStatusAvailable( |
| 138 provider->GetCategoryStatus(category))) { | 143 provider->GetCategoryStatus(category))) { |
| 139 suggestions_by_category_[category] = std::vector<ContentSuggestion>(); | 144 suggestions_by_category_[category] = std::vector<ContentSuggestion>(); |
| 140 } | 145 } |
| 141 NotifyCategoryStatusChanged(category); | 146 NotifyCategoryStatusChanged(category); |
| 142 } | 147 } |
| 148 std::sort(categories_.begin(), categories_.end(), |
| 149 [this](const ContentSuggestionsCategory& left, |
| 150 const ContentSuggestionsCategory& right) { |
| 151 return category_factory_.CompareCategories(left, right); |
| 152 }); |
| 143 provider->SetObserver(this); | 153 provider->SetObserver(this); |
| 144 } | 154 } |
| 145 | 155 |
| 146 //////////////////////////////////////////////////////////////////////////////// | 156 //////////////////////////////////////////////////////////////////////////////// |
| 147 // Private methods | 157 // Private methods |
| 148 | 158 |
| 149 void ContentSuggestionsService::OnNewSuggestions( | 159 void ContentSuggestionsService::OnNewSuggestions( |
| 150 ContentSuggestionsCategory changed_category, | 160 ContentSuggestionsCategory changed_category, |
| 151 std::vector<ContentSuggestion> new_suggestions) { | 161 std::vector<ContentSuggestion> new_suggestions) { |
| 152 DCHECK(IsCategoryRegistered(changed_category)); | 162 DCHECK(IsCategoryRegistered(changed_category)); |
| 153 | 163 |
| 154 for (const ContentSuggestion& suggestion : | 164 for (const ContentSuggestion& suggestion : |
| 155 suggestions_by_category_[changed_category]) { | 165 suggestions_by_category_[changed_category]) { |
| 156 id_category_map_.erase(suggestion.id()); | 166 id_category_map_.erase(suggestion.id()); |
| 157 } | 167 } |
| 158 | 168 |
| 159 for (const ContentSuggestion& suggestion : new_suggestions) { | 169 for (const ContentSuggestion& suggestion : new_suggestions) { |
| 160 id_category_map_[suggestion.id()] = changed_category; | 170 id_category_map_.insert(std::make_pair(suggestion.id(), changed_category)); |
| 161 } | 171 } |
| 162 | 172 |
| 163 suggestions_by_category_[changed_category] = std::move(new_suggestions); | 173 suggestions_by_category_[changed_category] = std::move(new_suggestions); |
| 164 | 174 |
| 165 FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions()); | 175 FOR_EACH_OBSERVER(Observer, observers_, OnNewSuggestions()); |
| 166 } | 176 } |
| 167 | 177 |
| 168 void ContentSuggestionsService::OnCategoryStatusChanged( | 178 void ContentSuggestionsService::OnCategoryStatusChanged( |
| 169 ContentSuggestionsCategory changed_category, | 179 ContentSuggestionsCategory changed_category, |
| 170 ContentSuggestionsCategoryStatus new_status) { | 180 ContentSuggestionsCategoryStatus new_status) { |
| 171 if (!IsContentSuggestionsCategoryStatusAvailable(new_status)) { | 181 if (!IsContentSuggestionsCategoryStatusAvailable(new_status)) { |
| 172 for (const ContentSuggestion& suggestion : | 182 for (const ContentSuggestion& suggestion : |
| 173 suggestions_by_category_[changed_category]) { | 183 suggestions_by_category_[changed_category]) { |
| 174 id_category_map_.erase(suggestion.id()); | 184 id_category_map_.erase(suggestion.id()); |
| 175 } | 185 } |
| 176 suggestions_by_category_.erase(changed_category); | 186 suggestions_by_category_.erase(changed_category); |
| 177 } | 187 } |
| 178 NotifyCategoryStatusChanged(changed_category); | 188 NotifyCategoryStatusChanged(changed_category); |
| 179 } | 189 } |
| 180 | 190 |
| 181 void ContentSuggestionsService::OnProviderShutdown( | 191 void ContentSuggestionsService::OnProviderShutdown( |
| 182 ContentSuggestionsProvider* provider) { | 192 ContentSuggestionsProvider* provider) { |
| 183 for (ContentSuggestionsCategory category : provider->provided_categories()) { | 193 for (ContentSuggestionsCategory category : |
| 194 provider->GetProvidedCategories()) { |
| 184 auto iterator = std::find(categories_.begin(), categories_.end(), category); | 195 auto iterator = std::find(categories_.begin(), categories_.end(), category); |
| 185 DCHECK(iterator != categories_.end()); | 196 DCHECK(iterator != categories_.end()); |
| 186 categories_.erase(iterator); | 197 categories_.erase(iterator); |
| 187 for (const ContentSuggestion& suggestion : | 198 for (const ContentSuggestion& suggestion : |
| 188 suggestions_by_category_[category]) { | 199 suggestions_by_category_[category]) { |
| 189 id_category_map_.erase(suggestion.id()); | 200 id_category_map_.erase(suggestion.id()); |
| 190 } | 201 } |
| 191 suggestions_by_category_.erase(category); | 202 suggestions_by_category_.erase(category); |
| 192 providers_.erase(category); | 203 providers_.erase(category); |
| 193 NotifyCategoryStatusChanged(category); | 204 NotifyCategoryStatusChanged(category); |
| 194 } | 205 } |
| 195 } | 206 } |
| 196 | 207 |
| 197 bool ContentSuggestionsService::IsCategoryRegistered( | 208 bool ContentSuggestionsService::IsCategoryRegistered( |
| 198 ContentSuggestionsCategory category) const { | 209 ContentSuggestionsCategory category) const { |
| 199 return std::find(categories_.begin(), categories_.end(), category) != | 210 return std::find(categories_.begin(), categories_.end(), category) != |
| 200 categories_.end(); | 211 categories_.end(); |
| 201 } | 212 } |
| 202 | 213 |
| 203 void ContentSuggestionsService::NotifyCategoryStatusChanged( | 214 void ContentSuggestionsService::NotifyCategoryStatusChanged( |
| 204 ContentSuggestionsCategory category) { | 215 ContentSuggestionsCategory category) { |
| 205 FOR_EACH_OBSERVER( | 216 FOR_EACH_OBSERVER( |
| 206 Observer, observers_, | 217 Observer, observers_, |
| 207 OnCategoryStatusChanged(category, GetCategoryStatus(category))); | 218 OnCategoryStatusChanged(category, GetCategoryStatus(category))); |
| 208 } | 219 } |
| 209 | 220 |
| 210 } // namespace ntp_snippets | 221 } // namespace ntp_snippets |
| OLD | NEW |