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 "chrome/browser/ntp_snippets/downloads/download_suggestions_provider.h" | |
6 | |
7 #include <algorithm> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/guid.h" | |
11 #include "base/strings/string_number_conversions.h" | |
12 #include "base/strings/string_util.h" | |
13 #include "base/strings/utf_string_conversions.h" | |
14 #include "base/threading/thread_task_runner_handle.h" | |
15 #include "base/time/time.h" | |
16 #include "components/ntp_snippets/pref_names.h" | |
17 #include "components/ntp_snippets/pref_util.h" | |
18 #include "components/offline_pages/client_namespace_constants.h" | |
19 #include "components/prefs/pref_registry_simple.h" | |
20 #include "components/prefs/pref_service.h" | |
21 #include "grit/components_strings.h" | |
22 #include "net/base/filename_util.h" | |
23 #include "ui/base/l10n/l10n_util.h" | |
24 #include "ui/gfx/image/image.h" | |
25 | |
26 using content::DownloadItem; | |
27 using content::DownloadManager; | |
28 using offline_pages::OfflinePageItem; | |
29 | |
30 namespace ntp_snippets { | |
31 | |
32 namespace { | |
33 | |
34 const int kMaxSuggestionsCount = 5; | |
Marc Treib
2016/10/11 12:07:40
This should probably be configurable via a variati
vitaliii
2016/10/13 08:43:06
Done.
| |
35 const char kAssetDownloadsPrefix[] = "D"; | |
36 const char kOfflinePageDownloadsPrefix[] = "O"; | |
Marc Treib
2016/10/11 12:07:40
Just make these two chars (not array)
vitaliii
2016/10/13 08:43:06
Done.
| |
37 | |
38 std::string GetOfflinePagePerCategoryID(int64_t raw_offline_page_id) { | |
39 // Raw ID is prefixed in order to avoid conflicts with asset downloads. | |
40 return base::JoinString( | |
41 {kOfflinePageDownloadsPrefix, base::IntToString(raw_offline_page_id)}, | |
42 ""); | |
Marc Treib
2016/10/11 12:07:41
s/""/std::string()/
But if you're just appending
vitaliii
2016/10/13 08:43:05
Done.
| |
43 } | |
44 | |
45 std::string GetAssetDownloadPerCategoryID(uint32_t raw_download_id) { | |
46 // Raw ID is prefixed in order to avoid conflicts with offline page downloads. | |
47 return base::JoinString( | |
48 {kAssetDownloadsPrefix, base::UintToString(raw_download_id)}, ""); | |
Marc Treib
2016/10/11 12:07:41
Same here
vitaliii
2016/10/13 08:43:05
Done.
| |
49 } | |
50 | |
51 // Determines whether |suggestion_id| corresponds to offline page suggestion or | |
52 // asset download based on |id_within_category| prefix. | |
53 bool IsOfflinePageID(const ContentSuggestion::ID& suggestion_id) { | |
Marc Treib
2016/10/11 12:07:40
nit: "OfflinePageID" is somewhat overloaded. Maybe
vitaliii
2016/10/13 08:43:05
Done.
| |
54 const std::string& id_within_category = suggestion_id.id_within_category(); | |
55 if (!id_within_category.empty()) { | |
56 if (id_within_category[0] == kOfflinePageDownloadsPrefix[0]) | |
57 return true; | |
58 if (id_within_category[0] == kAssetDownloadsPrefix[0]) | |
59 return false; | |
60 } | |
61 NOTREACHED() << "Unknown id_within_category " << id_within_category; | |
62 return false; | |
63 } | |
64 | |
65 struct OrderOfflinePagesByMostRecentlyVisitedFirst { | |
66 bool operator()(const OfflinePageItem* left, | |
67 const OfflinePageItem* right) const { | |
68 return left->last_access_time > right->last_access_time; | |
69 } | |
70 }; | |
71 | |
72 bool IsOfflinePageDownload(const offline_pages::ClientId& client_id) { | |
73 return (client_id.name_space == offline_pages::kAsyncNamespace || | |
74 client_id.name_space == offline_pages::kDownloadNamespace || | |
75 client_id.name_space == offline_pages::kNTPSuggestionsNamespace) && | |
Marc Treib
2016/10/11 12:07:41
What's kNTPSuggestionsNamespace?
vitaliii
2016/10/13 08:43:05
This is for offlined NTP items, e.g. right click o
| |
76 base::IsValidGUID(client_id.id); | |
Marc Treib
2016/10/11 12:07:41
Is this needed? Would we ever expect to get an inv
vitaliii
2016/10/13 08:43:06
It was done like that previously (and the logic wa
| |
77 } | |
78 | |
79 bool IsDownloadCompleted(const DownloadItem* item) { | |
Marc Treib
2016/10/11 12:07:40
nit: "const DownloadItem&" if it can't be null
vitaliii
2016/10/13 08:43:06
Wow, I did not know that this is possible with poi
| |
80 return item->GetState() == DownloadItem::DownloadState::COMPLETE && | |
81 !item->GetFileExternallyRemoved(); | |
82 } | |
83 | |
84 struct OrderDownloadsMostRecentlyDownloadedCompletedFirst { | |
85 bool operator()(const DownloadItem* left, const DownloadItem* right) const { | |
86 if (IsDownloadCompleted(left) != IsDownloadCompleted(right)) | |
87 return IsDownloadCompleted(left); | |
88 return left->GetEndTime() > right->GetEndTime(); | |
89 } | |
90 }; | |
91 | |
92 struct SuggestionItemWrapper { | |
93 base::Time time; | |
94 bool is_offline_page; | |
95 int index; | |
96 bool operator<(const SuggestionItemWrapper& other) const { | |
97 return time > other.time; | |
98 } | |
99 }; | |
100 | |
101 } // namespace | |
102 | |
103 DownloadSuggestionsProvider::DownloadSuggestionsProvider( | |
104 ContentSuggestionsProvider::Observer* observer, | |
105 CategoryFactory* category_factory, | |
106 const scoped_refptr<OfflinePageProxy>& offline_page_proxy, | |
107 content::DownloadManager* download_manager, | |
108 PrefService* pref_service, | |
109 bool download_manager_ui_enabled) | |
110 : ContentSuggestionsProvider(observer, category_factory), | |
111 category_status_(CategoryStatus::AVAILABLE_LOADING), | |
112 provided_category_( | |
113 category_factory->FromKnownCategory(KnownCategories::DOWNLOADS)), | |
114 offline_page_proxy_(offline_page_proxy), | |
115 download_manager_notifier_(download_manager, this), | |
116 pref_service_(pref_service), | |
117 download_manager_ui_enabled_(download_manager_ui_enabled), | |
118 weak_ptr_factory_(this) { | |
119 observer->OnCategoryStatusChanged(this, provided_category_, category_status_); | |
120 offline_page_proxy_->AddObserver(this); | |
121 FetchAllDownloadsAndSubmitSuggestions(); | |
122 } | |
123 | |
124 DownloadSuggestionsProvider::~DownloadSuggestionsProvider() { | |
125 offline_page_proxy_->RemoveObserver(this); | |
126 } | |
127 | |
128 CategoryStatus DownloadSuggestionsProvider::GetCategoryStatus( | |
129 Category category) { | |
130 if (category == provided_category_) | |
131 return category_status_; | |
132 NOTREACHED() << "Unknown category " << category.id(); | |
133 return CategoryStatus::NOT_PROVIDED; | |
Marc Treib
2016/10/11 12:07:40
Just do
DCHECK_EQ(provided_category_, category);
r
vitaliii
2016/10/13 08:43:05
Done.
| |
134 } | |
135 | |
136 CategoryInfo DownloadSuggestionsProvider::GetCategoryInfo(Category category) { | |
137 if (category == provided_category_) { | |
138 return CategoryInfo( | |
139 l10n_util::GetStringUTF16(IDS_NTP_DOWNLOAD_SUGGESTIONS_SECTION_HEADER), | |
140 ContentSuggestionsCardLayout::MINIMAL_CARD, | |
141 /*has_more_button=*/download_manager_ui_enabled_, | |
142 /*show_if_empty=*/false); | |
143 } | |
144 NOTREACHED() << "Unknown category " << category.id(); | |
Marc Treib
2016/10/11 12:07:40
Same here
vitaliii
2016/10/13 08:43:06
Done.
| |
145 return CategoryInfo(base::string16(), | |
146 ContentSuggestionsCardLayout::MINIMAL_CARD, | |
147 /*has_more_button=*/false, | |
148 /*show_if_empty=*/false); | |
149 } | |
150 | |
151 void DownloadSuggestionsProvider::DismissSuggestion( | |
152 const ContentSuggestion::ID& suggestion_id) { | |
153 DCHECK_EQ(provided_category_, suggestion_id.category()); | |
154 std::set<std::string> dismissed_ids = | |
155 ReadDismissedIDsFromPrefs(suggestion_id); | |
156 dismissed_ids.insert(suggestion_id.id_within_category()); | |
157 StoreDismissedIDsToPrefs(suggestion_id, dismissed_ids); | |
Marc Treib
2016/10/11 12:07:41
nit: I find it a bit confusing to pass in the sugg
vitaliii
2016/10/13 08:43:05
I did the former.
The latter is less attractive,
| |
158 | |
159 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id); | |
160 } | |
161 | |
162 void DownloadSuggestionsProvider::FetchSuggestionImage( | |
163 const ContentSuggestion::ID& suggestion_id, | |
164 const ImageFetchedCallback& callback) { | |
165 // TODO(vitaliii): Fetch proper thumbnail from OfflinePageModel once it is | |
166 // available there. | |
167 // TODO(vitaliii): Provide site's favicon for assets downloads. | |
168 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
169 FROM_HERE, base::Bind(callback, gfx::Image())); | |
170 } | |
171 | |
172 void DownloadSuggestionsProvider::ClearHistory( | |
173 base::Time begin, | |
174 base::Time end, | |
175 const base::Callback<bool(const GURL& url)>& filter) { | |
176 ClearDismissedSuggestionsForDebugging(provided_category_); | |
Marc Treib
2016/10/11 12:07:40
This will call FetchAllDownloadsAndSubmitSuggestio
vitaliii
2016/10/13 08:43:05
Good point! Actually current cache is completely i
Marc Treib
2016/10/13 12:11:25
Hm, but that is async, right? So maybe it's best t
vitaliii
2016/10/15 18:36:30
Done.
| |
177 cached_offline_page_downloads_.clear(); | |
178 cached_asset_downloads_.clear(); | |
179 FetchAllDownloadsAndSubmitSuggestions(); | |
180 } | |
181 | |
182 void DownloadSuggestionsProvider::ClearCachedSuggestions(Category category) { | |
183 DCHECK_EQ(provided_category_, category); | |
184 cached_offline_page_downloads_.clear(); | |
185 cached_asset_downloads_.clear(); | |
186 FetchAllDownloadsAndSubmitSuggestions(); | |
Marc Treib
2016/10/11 12:07:41
Would we expect this to change anything? The cache
vitaliii
2016/10/13 08:43:05
Good point.
| |
187 } | |
188 | |
189 void DownloadSuggestionsProvider::GetDismissedSuggestionsForDebugging( | |
190 Category category, | |
191 const DismissedSuggestionsCallback& callback) { | |
192 DCHECK_EQ(provided_category_, category); | |
193 // TODO(vitaliii): Implement. | |
194 callback.Run(std::vector<ContentSuggestion>()); | |
195 } | |
196 | |
197 void DownloadSuggestionsProvider::ClearDismissedSuggestionsForDebugging( | |
198 Category category) { | |
199 DCHECK_EQ(provided_category_, category); | |
200 StoreAssetDismissedIDsToPrefs(std::set<std::string>()); | |
201 StoreOfflinePageDismissedIDsToPrefs(std::set<std::string>()); | |
202 FetchAllDownloadsAndSubmitSuggestions(); | |
203 } | |
204 | |
205 // static | |
206 void DownloadSuggestionsProvider::RegisterProfilePrefs( | |
207 PrefRegistrySimple* registry) { | |
208 registry->RegisterListPref(prefs::kDismissedAssetDownloadSuggestions); | |
209 registry->RegisterListPref(prefs::kDismissedOfflinePageDownloadSuggestions); | |
210 } | |
211 | |
212 //////////////////////////////////////////////////////////////////////////////// | |
213 // Private methods | |
214 | |
215 void DownloadSuggestionsProvider::OfflinePageModelChanged( | |
216 const std::vector<offline_pages::OfflinePageItem>& offline_pages) { | |
217 NotifyStatusChanged(CategoryStatus::AVAILABLE); | |
Marc Treib
2016/10/11 12:07:41
SubmitContentSuggestions will change the status, s
vitaliii
2016/10/13 08:43:07
Yes, but it will happen just before the new sugges
Marc Treib
2016/10/13 12:11:25
But all this happens synchronously, so the UI does
vitaliii
2016/10/15 18:36:30
Done.
| |
218 ProcessAllOfflinePages(offline_pages); | |
219 SubmitContentSuggestions(); | |
220 } | |
221 | |
222 void DownloadSuggestionsProvider::OfflinePageDeleted( | |
223 int64_t offline_id, | |
224 const offline_pages::ClientId& client_id) { | |
225 // Because we never switch to NOT_PROVIDED dynamically, there can be no open | |
226 // UI containing an invalidated suggestion unless the status is something | |
227 // other than NOT_PROVIDED, so only notify invalidation in that case. | |
Marc Treib
2016/10/11 12:07:41
Actually, can the status *ever* be NOT_PROVIDED no
vitaliii
2016/10/13 08:43:05
Looks like not, but I am not sure.
However, I do
| |
228 if (category_status_ != CategoryStatus::NOT_PROVIDED && | |
229 IsOfflinePageDownload(client_id)) { | |
230 InvalidateSuggestion(GetOfflinePagePerCategoryID(offline_id)); | |
231 } | |
232 } | |
233 | |
234 void DownloadSuggestionsProvider::OnDownloadCreated(DownloadManager* manager, | |
235 DownloadItem* item) { | |
236 // This is called when new downloads are started and on startup for existing | |
237 // ones. | |
238 if (CacheAssetDownloadIfNeeded(item)) | |
239 SubmitContentSuggestions(); | |
240 } | |
241 | |
242 void DownloadSuggestionsProvider::OnDownloadUpdated(DownloadManager* manager, | |
243 DownloadItem* item) { | |
244 // Unfinished downloads may become completed, therefore, this call cannot be | |
245 // ignored. | |
246 if (CacheAssetDownloadIfNeeded(item)) | |
247 SubmitContentSuggestions(); | |
248 } | |
249 void DownloadSuggestionsProvider::OnDownloadOpened(DownloadManager* manager, | |
Marc Treib
2016/10/11 12:07:40
nit: empty line before
vitaliii
2016/10/13 08:43:06
Done.
| |
250 DownloadItem* item) { | |
251 // Ignored. | |
252 } | |
253 void DownloadSuggestionsProvider::OnDownloadRemoved(DownloadManager* manager, | |
Marc Treib
2016/10/11 12:07:40
Also here
vitaliii
2016/10/13 08:43:06
Done.
| |
254 DownloadItem* item) { | |
255 if (!IsDownloadCompleted(item)) | |
256 return; | |
257 // TODO(vitaliii): Implement a better way to clean up dismissed IDs (in case | |
258 // some calls are missed). | |
259 InvalidateSuggestion(GetAssetDownloadPerCategoryID(item->GetId())); | |
260 } | |
261 | |
262 void DownloadSuggestionsProvider::NotifyStatusChanged( | |
263 CategoryStatus new_status) { | |
264 DCHECK_NE(CategoryStatus::NOT_PROVIDED, category_status_); | |
Marc Treib
2016/10/11 12:07:40
Should this check new_status instead of category_s
vitaliii
2016/10/13 08:43:07
Done.
| |
265 if (category_status_ == new_status) | |
266 return; | |
267 category_status_ = new_status; | |
268 observer()->OnCategoryStatusChanged(this, provided_category_, new_status); | |
Marc Treib
2016/10/11 12:07:40
nit: s/new_status/category_status_/
vitaliii
2016/10/13 08:43:06
Done.
| |
269 } | |
270 | |
271 void DownloadSuggestionsProvider::FetchOfflinePagesDownloads() { | |
272 offline_page_proxy_->GetAllPages( | |
273 base::Bind(&DownloadSuggestionsProvider::ProcessAllOfflinePages, | |
274 weak_ptr_factory_.GetWeakPtr())); | |
275 } | |
276 | |
277 void DownloadSuggestionsProvider:: | |
278 FetchOfflinePagesDownloadsAndSubmitSuggestions() { | |
279 offline_page_proxy_->GetAllPages( | |
280 base::Bind(&DownloadSuggestionsProvider::OfflinePageModelChanged, | |
281 weak_ptr_factory_.GetWeakPtr())); | |
282 } | |
283 | |
284 void DownloadSuggestionsProvider::FetchAssetsDownloads() { | |
285 DownloadManager* manager = download_manager_notifier_.GetManager(); | |
286 if (!manager) { | |
287 // The manager has gone down. | |
288 return; | |
289 } | |
290 | |
291 NotifyStatusChanged(CategoryStatus::AVAILABLE); | |
Marc Treib
2016/10/11 12:07:41
Why is this here?
vitaliii
2016/10/13 08:43:07
Done.
| |
292 | |
293 std::vector<DownloadItem*> downloads; | |
294 manager->GetAllDownloads(&downloads); | |
295 | |
296 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs(); | |
297 std::set<std::string> new_dismissed_ids; | |
Marc Treib
2016/10/11 12:07:40
Maybe old_dismissed_ids and retained_dismissed_ids
vitaliii
2016/10/13 08:43:06
Done.
| |
298 std::vector<DownloadItem*> not_dismissed_downloads; | |
299 for (DownloadItem* item : downloads) { | |
Marc Treib
2016/10/11 12:07:41
const?
vitaliii
2016/10/13 08:43:05
Done.
| |
300 std::string within_category_id = | |
301 GetAssetDownloadPerCategoryID(item->GetId()); | |
302 if (!dismissed_ids.count(within_category_id)) { | |
303 not_dismissed_downloads.push_back(item); | |
304 } else { | |
305 new_dismissed_ids.insert(within_category_id); | |
306 } | |
307 } | |
308 | |
309 if (dismissed_ids.size() != new_dismissed_ids.size()) | |
310 StoreAssetDismissedIDsToPrefs(new_dismissed_ids); | |
311 | |
312 downloads = not_dismissed_downloads; | |
Marc Treib
2016/10/11 12:07:40
Maybe rename "downloads" to "all_downloads", "not_
vitaliii
2016/10/13 08:43:06
Done.
| |
313 if (static_cast<int>(downloads.size()) > kMaxSuggestionsCount) { | |
314 std::nth_element(downloads.begin(), | |
Marc Treib
2016/10/11 12:07:41
Nice!
But since nth_element isn't very common, and
vitaliii
2016/10/13 08:43:06
Done.
| |
315 downloads.begin() + kMaxSuggestionsCount, downloads.end(), | |
316 OrderDownloadsMostRecentlyDownloadedCompletedFirst()); | |
317 downloads.resize(kMaxSuggestionsCount); | |
318 } | |
319 | |
320 cached_asset_downloads_.clear(); | |
321 for (DownloadItem* item : downloads) { | |
322 if (IsDownloadCompleted(item)) | |
Marc Treib
2016/10/11 12:07:41
Wouldn't it be easier if the filtering happened be
vitaliii
2016/10/13 08:43:06
Done.
| |
323 cached_asset_downloads_.push_back(item); | |
324 } | |
325 } | |
326 | |
327 void DownloadSuggestionsProvider::FetchAllDownloadsAndSubmitSuggestions() { | |
328 FetchAssetsDownloads(); | |
329 FetchOfflinePagesDownloadsAndSubmitSuggestions(); | |
Marc Treib
2016/10/11 12:07:41
I was gonna say that this isn't safe since the off
vitaliii
2016/10/13 08:43:05
Done.
| |
330 } | |
331 | |
332 void DownloadSuggestionsProvider::SubmitContentSuggestions() { | |
333 NotifyStatusChanged(CategoryStatus::AVAILABLE); | |
334 | |
335 std::vector<SuggestionItemWrapper> suggestion_items; | |
336 for (int i = 0; i < static_cast<int>(cached_offline_page_downloads_.size()); | |
337 ++i) { | |
338 SuggestionItemWrapper wrapped_item; | |
339 wrapped_item.is_offline_page = true; | |
340 wrapped_item.index = i; | |
Marc Treib
2016/10/11 12:07:41
Mh, this is a bit yucky. Maybe instead of bool+ind
vitaliii
2016/10/13 08:43:05
Acknowledged.
Marc Treib
2016/10/13 12:11:25
...this reply confused me ;P
"Obsolete, Suggestion
vitaliii
2016/10/15 18:36:29
I've got it. Sorry about that.
| |
341 wrapped_item.time = cached_offline_page_downloads_[i].last_access_time; | |
342 suggestion_items.push_back(wrapped_item); | |
343 } | |
344 | |
345 for (int i = 0; i < static_cast<int>(cached_asset_downloads_.size()); ++i) { | |
346 SuggestionItemWrapper wrapped_item; | |
347 wrapped_item.is_offline_page = false; | |
348 wrapped_item.index = i; | |
349 wrapped_item.time = cached_asset_downloads_[i]->GetEndTime(); | |
350 suggestion_items.push_back(wrapped_item); | |
351 } | |
352 | |
353 std::sort(suggestion_items.begin(), suggestion_items.end()); | |
Marc Treib
2016/10/11 12:07:40
Or in fact: Can the sorting just happen after conv
vitaliii
2016/10/13 08:43:06
Done.
| |
354 | |
355 std::vector<ContentSuggestion> suggestions; | |
356 for (const SuggestionItemWrapper& wrapped_item : suggestion_items) { | |
357 if (suggestions.size() >= kMaxSuggestionsCount) | |
358 break; | |
359 | |
360 if (wrapped_item.is_offline_page) { | |
361 suggestions.push_back(ConvertOfflinePage( | |
362 cached_offline_page_downloads_[wrapped_item.index])); | |
363 } else { | |
364 suggestions.push_back( | |
365 ConvertDownloadItem(cached_asset_downloads_[wrapped_item.index])); | |
366 } | |
367 } | |
368 | |
369 observer()->OnNewSuggestions(this, provided_category_, | |
370 std::move(suggestions)); | |
371 } | |
372 | |
373 ContentSuggestion DownloadSuggestionsProvider::ConvertOfflinePage( | |
374 const OfflinePageItem& offline_page) const { | |
375 // TODO(vitaliii): Make sure the URL is actually opened as an offline URL even | |
376 // when the user is online. | |
Marc Treib
2016/10/11 12:07:40
Do we have a bug for this? If not, could you file
vitaliii
2016/10/13 08:43:06
Done.
Why do not you like
https://cs.chromium.org
Marc Treib
2016/10/13 12:11:25
TODOs in the code aren't a good issue tracking sys
vitaliii
2016/10/15 18:36:29
I see, good point.
| |
377 ContentSuggestion suggestion( | |
378 ContentSuggestion::ID(provided_category_, GetOfflinePagePerCategoryID( | |
379 offline_page.offline_id)), | |
380 offline_page.url); | |
381 | |
382 if (offline_page.title.empty()) { | |
383 // TODO(vitaliii): Remove this fallback once the OfflinePageModel provides | |
384 // titles for all (relevant) OfflinePageItems. | |
385 suggestion.set_title(base::UTF8ToUTF16(offline_page.url.spec())); | |
386 } else { | |
387 suggestion.set_title(offline_page.title); | |
388 } | |
389 suggestion.set_publish_date(offline_page.creation_time); | |
390 suggestion.set_publisher_name(base::UTF8ToUTF16(offline_page.url.host())); | |
391 return suggestion; | |
392 } | |
393 | |
394 ContentSuggestion DownloadSuggestionsProvider::ConvertDownloadItem( | |
395 const DownloadItem* download_item) const { | |
396 ContentSuggestion::ID per_category_id( | |
Marc Treib
2016/10/11 12:07:40
This is not a per-category-ID. But I'd just inline
vitaliii
2016/10/13 08:43:05
Done.
| |
397 provided_category_, | |
398 GetAssetDownloadPerCategoryID(download_item->GetId())); | |
399 // TODO(vitaliii): Ensure that files are opened in browser, but not downloaded | |
400 // again. | |
401 ContentSuggestion suggestion( | |
402 per_category_id, | |
403 net::FilePathToFileURL(download_item->GetTargetFilePath())); | |
404 // TODO(vitaliii): Set proper title. | |
405 suggestion.set_title( | |
406 base::UTF8ToUTF16(download_item->GetTargetFilePath().BaseName().value())); | |
Marc Treib
2016/10/11 12:07:40
I think this won't work on all platforms, since pa
vitaliii
2016/10/13 08:43:06
This is Anroid only for now, added DCHECK for futu
| |
407 suggestion.set_publish_date(download_item->GetEndTime()); | |
408 suggestion.set_publisher_name( | |
409 base::UTF8ToUTF16(download_item->GetURL().host())); | |
410 // TODO(vitaliii): Set suggestion icon. | |
411 return suggestion; | |
412 } | |
413 | |
414 bool DownloadSuggestionsProvider::CacheAssetDownloadIfNeeded( | |
415 const content::DownloadItem* item) { | |
416 if (!IsDownloadCompleted(item)) | |
417 return false; | |
418 | |
419 std::vector<const content::DownloadItem*>& cache = cached_asset_downloads_; | |
Marc Treib
2016/10/11 12:07:41
Please avoid non-const references in general.
Here
vitaliii
2016/10/13 08:43:06
Done.
| |
420 if (std::find(cache.begin(), cache.end(), item) != cache.end()) | |
Marc Treib
2016/10/11 12:07:41
optional: base::ContainsValue might be a bit easie
vitaliii
2016/10/13 08:43:07
Done.
| |
421 return false; | |
422 | |
423 std::set<std::string> dismissed_ids = ReadAssetDismissedIDsFromPrefs(); | |
424 if (dismissed_ids.count(GetAssetDownloadPerCategoryID(item->GetId()))) | |
425 return false; | |
426 | |
427 DCHECK(cache.size() <= kMaxSuggestionsCount); | |
Marc Treib
2016/10/11 12:07:40
DCHECK_LE?
vitaliii
2016/10/13 08:43:06
Done.
| |
428 if (cache.size() == kMaxSuggestionsCount) { | |
429 std::vector<const content::DownloadItem*>::iterator oldest = | |
Marc Treib
2016/10/11 12:07:40
optional: auto oldest_it = ...?
vitaliii
2016/10/13 08:43:05
Done.
| |
430 std::max_element(cache.begin(), cache.end(), | |
431 OrderDownloadsMostRecentlyDownloadedCompletedFirst()); | |
432 if (item->GetEndTime() <= (*oldest)->GetEndTime()) | |
vitaliii
2016/10/11 08:15:56
Would using an index here be better?
Marc Treib
2016/10/11 12:07:41
I don't follow - an index for what?
vitaliii
2016/10/13 08:43:06
i.e.
int i = oldest - cached_asset_downloads_.beg
Marc Treib
2016/10/13 12:11:25
Ah - no, what you have is fine.
vitaliii
2016/10/15 18:36:29
Acknowledged.
| |
433 return false; | |
434 | |
435 (*oldest) = item; | |
Marc Treib
2016/10/11 12:07:41
nit: parens not required
vitaliii
2016/10/13 08:43:05
Done.
| |
436 } else { | |
437 cache.push_back(item); | |
438 } | |
439 | |
440 return true; | |
441 } | |
442 | |
443 bool DownloadSuggestionsProvider::RemoveSuggestionFromCacheIfPresent( | |
444 const ContentSuggestion::ID& suggestion_id) { | |
445 DCHECK_EQ(provided_category_, suggestion_id.category()); | |
446 if (IsOfflinePageID(suggestion_id)) { | |
447 std::vector<offline_pages::OfflinePageItem>& cache = | |
Marc Treib
2016/10/11 12:07:41
As above: Please spell out cached_offline_page_dow
vitaliii
2016/10/13 08:43:06
Done.
| |
448 cached_offline_page_downloads_; | |
449 for (int i = 0; i < static_cast<int>(cache.size()); ++i) { | |
Marc Treib
2016/10/11 12:07:40
optional: This might be slightly nicer as an std::
vitaliii
2016/10/13 08:43:06
Done.
| |
450 if (GetOfflinePagePerCategoryID(cache[i].offline_id) == | |
451 suggestion_id.id_within_category()) { | |
452 cache.erase(cache.begin() + i); | |
453 return true; | |
454 } | |
455 } | |
456 return false; | |
457 } else { | |
Marc Treib
2016/10/11 12:07:40
No "else" after "return"
vitaliii
2016/10/13 08:43:06
Done.
| |
458 std::vector<const content::DownloadItem*>& cache = cached_asset_downloads_; | |
Marc Treib
2016/10/11 12:07:40
Also here: Please spell out
vitaliii
2016/10/13 08:43:05
Done.
| |
459 for (int i = 0; i < static_cast<int>(cache.size()); ++i) { | |
460 if (GetAssetDownloadPerCategoryID(cache[i]->GetId()) == | |
461 suggestion_id.id_within_category()) { | |
462 cache.erase(cache.begin() + i); | |
463 return true; | |
464 } | |
465 } | |
466 return false; | |
467 } | |
468 } | |
469 | |
470 void DownloadSuggestionsProvider:: | |
471 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded( | |
472 const ContentSuggestion::ID& suggestion_id) { | |
473 DCHECK_EQ(provided_category_, suggestion_id.category()); | |
474 if (RemoveSuggestionFromCacheIfPresent(suggestion_id)) { | |
Marc Treib
2016/10/11 12:07:41
if (!Remove...)
return;
and save a level of inde
vitaliii
2016/10/13 08:43:07
Done.
| |
475 if (IsOfflinePageID(suggestion_id)) { | |
476 if (cached_offline_page_downloads_.size() == kMaxSuggestionsCount - 1) { | |
477 // Previously there were |kMaxSuggestionsCount| cached suggestion, | |
478 // therefore, overall there may be more than |kMaxSuggestionsCount| | |
479 // suggestions in the model and now one of them may be cached instead of | |
480 // the removed one. Even though, the suggestions are not immediately | |
481 // used the cache has to be kept up to date, because it may be used when | |
482 // other data source is updated. | |
483 FetchOfflinePagesDownloads(); | |
484 } | |
485 } else { | |
486 if (cached_asset_downloads_.size() == kMaxSuggestionsCount - 1) { | |
487 // The same as the case above. | |
488 FetchAssetsDownloads(); | |
489 } | |
490 } | |
491 } | |
492 } | |
493 | |
494 void DownloadSuggestionsProvider::ProcessAllOfflinePages( | |
495 const std::vector<offline_pages::OfflinePageItem>& all_offline_pages) { | |
496 std::set<std::string> old_dismissed_ids = | |
497 ReadOfflinePageDismissedIDsFromPrefs(); | |
498 std::set<std::string> new_dismissed_ids; | |
499 std::vector<const OfflinePageItem*> items; | |
500 // Filtering out dismissed items and pruning dismissed IDs. | |
501 for (const OfflinePageItem& item : all_offline_pages) { | |
502 if (!IsOfflinePageDownload(item.client_id)) | |
503 continue; | |
504 | |
505 std::string per_category_id = GetOfflinePagePerCategoryID(item.offline_id); | |
506 if (!old_dismissed_ids.count(per_category_id)) | |
Marc Treib
2016/10/11 12:07:41
I think the dismissed IDs sometimes have the "O"/"
vitaliii
2016/10/13 08:43:05
For the former - I do not think so, I added DCHECK
Marc Treib
2016/10/13 12:11:25
I looked over all the code, and it looks like you
vitaliii
2016/10/15 18:36:29
Let's stick with this approach for now and then I
| |
507 items.push_back(&item); | |
508 else | |
509 new_dismissed_ids.insert(per_category_id); | |
510 } | |
511 | |
512 if (static_cast<int>(items.size()) > kMaxSuggestionsCount) { | |
513 std::nth_element(items.begin(), items.begin() + kMaxSuggestionsCount, | |
514 items.end(), | |
515 OrderOfflinePagesByMostRecentlyVisitedFirst()); | |
Marc Treib
2016/10/11 12:07:41
You could use a lambda here
vitaliii
2016/10/13 08:43:04
Done.
| |
516 items.resize(kMaxSuggestionsCount); | |
517 } | |
518 | |
519 cached_offline_page_downloads_.clear(); | |
520 for (const OfflinePageItem* item : items) { | |
521 cached_offline_page_downloads_.push_back(*item); | |
522 } | |
523 | |
524 if (old_dismissed_ids.size() != new_dismissed_ids.size()) { | |
525 StoreOfflinePageDismissedIDsToPrefs(new_dismissed_ids); | |
526 } | |
527 } | |
528 | |
529 void DownloadSuggestionsProvider::InvalidateSuggestion( | |
530 const std::string& per_category_id) { | |
531 ContentSuggestion::ID suggestion_id(provided_category_, per_category_id); | |
532 observer()->OnSuggestionInvalidated(this, suggestion_id); | |
533 | |
534 std::set<std::string> dismissed_ids = | |
535 ReadDismissedIDsFromPrefs(suggestion_id); | |
536 auto it = dismissed_ids.find(per_category_id); | |
537 if (it != dismissed_ids.end()) { | |
538 dismissed_ids.erase(it); | |
539 StoreDismissedIDsToPrefs(suggestion_id, dismissed_ids); | |
540 } | |
541 | |
542 RemoveSuggestionFromCacheAndRetrieveMoreIfNeeded(suggestion_id); | |
543 } | |
544 | |
545 std::set<std::string> | |
546 DownloadSuggestionsProvider::ReadAssetDismissedIDsFromPrefs() const { | |
547 return prefs::ReadDismissedIDsFromPrefs( | |
548 *pref_service_, prefs::kDismissedAssetDownloadSuggestions); | |
549 } | |
550 | |
551 void DownloadSuggestionsProvider::StoreAssetDismissedIDsToPrefs( | |
552 const std::set<std::string>& dismissed_ids) { | |
553 prefs::StoreDismissedIDsToPrefs( | |
554 pref_service_, prefs::kDismissedAssetDownloadSuggestions, dismissed_ids); | |
555 } | |
556 | |
557 std::set<std::string> | |
558 DownloadSuggestionsProvider::ReadOfflinePageDismissedIDsFromPrefs() const { | |
559 return prefs::ReadDismissedIDsFromPrefs( | |
560 *pref_service_, prefs::kDismissedOfflinePageDownloadSuggestions); | |
561 } | |
562 | |
563 void DownloadSuggestionsProvider::StoreOfflinePageDismissedIDsToPrefs( | |
564 const std::set<std::string>& dismissed_ids) { | |
565 prefs::StoreDismissedIDsToPrefs( | |
566 pref_service_, prefs::kDismissedOfflinePageDownloadSuggestions, | |
567 dismissed_ids); | |
568 } | |
569 | |
570 std::set<std::string> DownloadSuggestionsProvider::ReadDismissedIDsFromPrefs( | |
571 const ContentSuggestion::ID& suggestion_id) const { | |
572 if (IsOfflinePageID(suggestion_id)) { | |
573 return ReadOfflinePageDismissedIDsFromPrefs(); | |
574 } else { | |
Marc Treib
2016/10/11 12:07:40
nit: No "else" after "return"
Or maybe just get r
vitaliii
2016/10/13 08:43:06
Done.
| |
575 return ReadAssetDismissedIDsFromPrefs(); | |
576 } | |
577 } | |
578 | |
579 void DownloadSuggestionsProvider::StoreDismissedIDsToPrefs( | |
580 const ContentSuggestion::ID& suggestion_id, | |
581 const std::set<std::string>& dismissed_ids) { | |
582 if (IsOfflinePageID(suggestion_id)) { | |
583 StoreOfflinePageDismissedIDsToPrefs(dismissed_ids); | |
584 } else { | |
Marc Treib
2016/10/11 12:07:39
Also here
vitaliii
2016/10/13 08:43:05
Done.
| |
585 StoreAssetDismissedIDsToPrefs(dismissed_ids); | |
586 } | |
587 } | |
588 | |
589 } // namespace ntp_snippets | |
OLD | NEW |