OLD | NEW |
---|---|
(Empty) | |
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 | |
3 // found in the LICENSE file. | |
4 | |
5 #include "components/ntp_snippets/content_suggestions_service.h" | |
6 | |
7 #include <algorithm> | |
8 #include <iterator> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "ui/gfx/image/image.h" | |
13 | |
14 namespace ntp_snippets { | |
15 | |
16 namespace { | |
17 | |
18 // Helper method get the provider_type from a combined ID. | |
tschumann
2016/07/01 09:37:11
nit: let's keep the comment focused on the essenti
Philipp Keck
2016/07/01 13:00:04
Done.
| |
19 ContentSuggestionsProviderType GetProviderTypeFromCombinedID( | |
20 const std::string& id) { | |
21 size_t colon_index = id.find(":"); | |
22 DCHECK_NE(std::string::npos, colon_index); | |
23 int provider_type; | |
24 DCHECK(base::StringToInt(id.substr(0, colon_index), &provider_type)); | |
25 DCHECK(0 <= provider_type && | |
26 provider_type < int(ContentSuggestionsProviderType::COUNT)); | |
27 return ContentSuggestionsProviderType(provider_type); | |
28 } | |
29 | |
30 // Helper method get the original ID from a combined ID. | |
31 std::string GetIDFromCombinedID(const std::string& id) { | |
32 size_t colon_index = id.find(":"); | |
33 DCHECK_NE(std::string::npos, colon_index); | |
34 return id.substr(colon_index + 1); | |
35 } | |
36 | |
37 // Helper method to remove all suggestions of the given |provider_type| from | |
38 // the given |suggestions|. Returns true if anything changed. | |
39 bool RemoveSuggestionsOfProvider(std::vector<ContentSuggestion>* suggestions, | |
40 ContentSuggestionsProviderType provider_type) { | |
41 auto remove_from = std::remove_if( | |
42 suggestions->begin(), suggestions->end(), | |
43 [provider_type](const ContentSuggestion& s) { | |
44 return GetProviderTypeFromCombinedID(s.id()) == provider_type; | |
45 }); | |
46 if (remove_from == suggestions->end()) { | |
47 return false; | |
48 } | |
49 suggestions->erase(remove_from, suggestions->end()); | |
50 return true; | |
51 } | |
52 | |
53 } // namespace | |
54 | |
55 ContentSuggestionsService::ContentSuggestionsService(Enabled enabled) | |
56 : enabled_(enabled) {} | |
57 | |
58 ContentSuggestionsService::~ContentSuggestionsService() {} | |
59 | |
60 void ContentSuggestionsService::Shutdown() { | |
61 FOR_EACH_OBSERVER(Observer, observers_, ContentSuggestionsServiceShutdown()); | |
62 observers_.Clear(); | |
63 for (auto& provider : providers_) { | |
tschumann
2016/07/01 09:37:11
Too bad we don't have value_view in Chrome (https:
Philipp Keck
2016/07/01 13:00:04
Acknowledged.
| |
64 provider.second->SetObserver(nullptr); | |
65 } | |
66 providers_.clear(); | |
67 enabled_ = Enabled::Disable; | |
68 } | |
69 | |
70 const std::vector<ContentSuggestion>& | |
71 ContentSuggestionsService::GetSuggestionsForCategory( | |
72 ContentSuggestionCategory category) const { | |
73 return suggestions_by_category_.at(category); | |
74 } | |
75 | |
76 void ContentSuggestionsService::FetchSuggestionImage( | |
77 const std::string& suggestion_id, | |
78 const ImageFetchedCallback& callback) { | |
79 ContentSuggestionsProviderType provider_type = | |
80 GetProviderTypeFromCombinedID(suggestion_id); | |
81 if (providers_.count(provider_type)) { | |
82 std::string original_id = GetIDFromCombinedID(suggestion_id); | |
83 providers_[provider_type]->FetchSuggestionImage( | |
84 original_id, base::Bind(callback, suggestion_id)); | |
85 } else { | |
86 LOG(WARNING) << "Requested image for suggestion " << suggestion_id | |
87 << " for unavailable provider type " << int(provider_type); | |
88 callback.Run(suggestion_id, gfx::Image()); | |
89 } | |
90 } | |
91 | |
92 void ContentSuggestionsService::ClearCachedSuggestionsForDebugging() { | |
93 categories_.clear(); | |
94 suggestions_by_category_.clear(); | |
95 for (auto& provider : providers_) { | |
96 provider.second->ClearCachedSuggestionsForDebugging(); | |
97 } | |
98 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged()); | |
99 } | |
100 | |
101 void ContentSuggestionsService::ClearDiscardedSuggestionsForDebugging() { | |
102 for (auto& provider : providers_) { | |
103 provider.second->ClearDiscardedSuggestionsForDebugging(); | |
104 } | |
105 } | |
106 | |
107 void ContentSuggestionsService::DiscardSuggestion( | |
108 const std::string& suggestion_id) { | |
109 ContentSuggestionsProviderType provider_type = | |
110 GetProviderTypeFromCombinedID(suggestion_id); | |
111 std::string original_id = GetIDFromCombinedID(suggestion_id); | |
112 | |
113 if (providers_.count(provider_type)) { | |
114 providers_[provider_type]->DiscardSuggestion(original_id); | |
115 | |
116 // Remove the suggestion locally. | |
117 for (auto& pair : suggestions_by_category_) { | |
118 auto position = | |
119 std::find_if(pair.second.begin(), pair.second.end(), | |
120 [&suggestion_id](const ContentSuggestion& suggestion) { | |
121 return suggestion_id == suggestion.id(); | |
122 }); | |
123 if (position != pair.second.end()) { | |
124 pair.second.erase(position); | |
125 } | |
126 } | |
127 } else { | |
128 LOG(WARNING) << "Discarded suggestion " << suggestion_id | |
129 << " for unavailable provider type " << int(provider_type); | |
130 } | |
131 } | |
132 | |
133 void ContentSuggestionsService::AddObserver(Observer* observer) { | |
134 observers_.AddObserver(observer); | |
135 } | |
136 | |
137 void ContentSuggestionsService::RemoveObserver(Observer* observer) { | |
138 observers_.RemoveObserver(observer); | |
139 } | |
140 | |
141 void ContentSuggestionsService::RegisterProvider( | |
142 ContentSuggestionsProvider* provider) { | |
143 DCHECK_EQ(0ul, providers_.count(provider->GetProviderType())); | |
144 providers_[provider->GetProviderType()] = provider; | |
145 provider->SetObserver(this); | |
146 } | |
147 | |
148 //////////////////////////////////////////////////////////////////////////////// | |
149 // Private methods | |
150 | |
151 void ContentSuggestionsService::OnSuggestionsChanged( | |
152 ContentSuggestionsProviderType provider_type, | |
153 ContentSuggestionCategory changed_category, | |
154 std::vector<ContentSuggestion> new_suggestions) { | |
155 RemoveSuggestionsOfProvider(&suggestions_by_category_[changed_category], | |
156 provider_type); | |
157 | |
158 std::move(new_suggestions.begin(), new_suggestions.end(), | |
159 std::back_inserter(suggestions_by_category_[changed_category])); | |
160 | |
161 // Ensure the category exists exactly if its list of suggestions is nonempty. | |
162 auto position = | |
163 std::find(categories_.begin(), categories_.end(), changed_category); | |
164 if (suggestions_by_category_[changed_category].empty()) { | |
165 suggestions_by_category_.erase(changed_category); | |
166 if (position != categories_.end()) { | |
167 categories_.erase(position); | |
168 } | |
169 } else { | |
170 if (position == categories_.end()) { | |
171 // TODO(pke) In the future, make sure that the categories have some useful | |
172 // (maybe constant, at least consistent) ordering for the UI. | |
173 categories_.emplace_back(changed_category); | |
174 } | |
175 } | |
176 | |
177 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged()); | |
178 } | |
179 | |
180 void ContentSuggestionsService::OnProviderShutdown( | |
181 ContentSuggestionsProviderType provider_type) { | |
182 auto iterator = suggestions_by_category_.begin(); | |
183 while (iterator != suggestions_by_category_.end()) { | |
184 if (RemoveSuggestionsOfProvider(&iterator->second, provider_type) && | |
185 iterator->second.empty()) { | |
186 categories_.erase( | |
187 std::find(categories_.begin(), categories_.end(), iterator->first)); | |
188 iterator = suggestions_by_category_.erase(iterator); | |
189 } else { | |
190 ++iterator; | |
191 } | |
192 } | |
193 FOR_EACH_OBSERVER(Observer, observers_, OnSuggestionsChanged()); | |
194 providers_.erase(provider_type); | |
195 // TODO(pke) Notify the UI to do a force refresh in this case, as the | |
196 // suggestions must disappear immediately. | |
197 } | |
198 | |
199 } // namespace ntp_snippets | |
OLD | NEW |