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 package org.chromium.chrome.browser.ntp.cards; | 5 package org.chromium.chrome.browser.ntp.cards; |
6 | 6 |
7 import org.chromium.base.Log; | 7 import org.chromium.base.Log; |
8 import org.chromium.base.VisibleForTesting; | 8 import org.chromium.base.VisibleForTesting; |
9 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; | 9 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; |
10 import org.chromium.chrome.browser.ntp.snippets.CategoryInt; | 10 import org.chromium.chrome.browser.ntp.snippets.CategoryInt; |
(...skipping 14 matching lines...) Expand all Loading... | |
25 * suggestions source and updates the corresponding sections. | 25 * suggestions source and updates the corresponding sections. |
26 */ | 26 */ |
27 public class SectionList | 27 public class SectionList |
28 extends InnerNode implements SuggestionsSource.Observer, SuggestionsSect ion.Delegate { | 28 extends InnerNode implements SuggestionsSource.Observer, SuggestionsSect ion.Delegate { |
29 private static final String TAG = "Ntp"; | 29 private static final String TAG = "Ntp"; |
30 | 30 |
31 /** Maps suggestion categories to sections, with stable iteration ordering. */ | 31 /** Maps suggestion categories to sections, with stable iteration ordering. */ |
32 private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap <>(); | 32 private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap <>(); |
33 private final NewTabPageManager mNewTabPageManager; | 33 private final NewTabPageManager mNewTabPageManager; |
34 private final OfflinePageBridge mOfflinePageBridge; | 34 private final OfflinePageBridge mOfflinePageBridge; |
35 private final SuggestionRanker mSuggestionRanker; | |
35 | 36 |
36 public SectionList(NewTabPageManager newTabPageManager, OfflinePageBridge of flinePageBridge) { | 37 public SectionList(NewTabPageManager newTabPageManager, OfflinePageBridge of flinePageBridge) { |
38 mSuggestionRanker = new SuggestionRanker(); | |
37 mNewTabPageManager = newTabPageManager; | 39 mNewTabPageManager = newTabPageManager; |
38 mNewTabPageManager.getSuggestionsSource().setObserver(this); | 40 mNewTabPageManager.getSuggestionsSource().setObserver(this); |
39 mOfflinePageBridge = offlinePageBridge; | 41 mOfflinePageBridge = offlinePageBridge; |
40 resetSections(/* alwaysAllowEmptySections = */ false); | 42 resetSections(/* alwaysAllowEmptySections = */ false); |
41 } | 43 } |
42 | 44 |
43 /** | 45 /** |
44 * Resets the sections, reloading the whole new tab page content. | 46 * Resets the sections, reloading the whole new tab page content. |
45 * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when | 47 * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when |
46 * they are empty, even when they are normally not. | 48 * they are empty, even when they are normally not. |
47 */ | 49 */ |
48 public void resetSections(boolean alwaysAllowEmptySections) { | 50 public void resetSections(boolean alwaysAllowEmptySections) { |
49 removeAllSections(); | 51 removeAllSections(); |
50 | 52 |
51 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource(); | 53 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource(); |
52 int[] categories = suggestionsSource.getCategories(); | 54 int[] categories = suggestionsSource.getCategories(); |
53 int[] suggestionsPerCategory = new int[categories.length]; | 55 int[] suggestionsPerCategory = new int[categories.length]; |
54 int i = 0; | 56 int i = 0; |
55 for (int category : categories) { | 57 for (int category : categories) { |
56 int categoryStatus = suggestionsSource.getCategoryStatus(category); | 58 int categoryStatus = suggestionsSource.getCategoryStatus(category); |
57 if (categoryStatus == CategoryStatus.LOADING_ERROR | 59 if (categoryStatus == CategoryStatus.LOADING_ERROR |
58 || categoryStatus == CategoryStatus.NOT_PROVIDED | 60 || categoryStatus == CategoryStatus.NOT_PROVIDED |
59 || categoryStatus == CategoryStatus.CATEGORY_EXPLICITLY_DISA BLED) | 61 || categoryStatus == CategoryStatus.CATEGORY_EXPLICITLY_DISA BLED) |
60 continue; | 62 continue; |
61 | 63 |
62 suggestionsPerCategory[i++] = | 64 suggestionsPerCategory[i++] = |
63 resetSection(category, categoryStatus, alwaysAllowEmptySecti ons); | 65 resetSection(category, categoryStatus, alwaysAllowEmptySecti ons); |
64 } | 66 } |
65 | 67 |
66 mNewTabPageManager.trackSnippetsPageImpression(categories, suggestionsPe rCategory); | 68 mNewTabPageManager.getSuggestionsMetricsReporter().onPageShown( |
69 categories, suggestionsPerCategory); | |
67 } | 70 } |
68 | 71 |
69 /** | 72 /** |
70 * Resets the section for {@code category}. Removes the section if there are no suggestions for | 73 * Resets the section for {@code category}. Removes the section if there are no suggestions for |
71 * it and it is not allowed to be empty. Otherwise, creates the section if i t is not present | 74 * it and it is not allowed to be empty. Otherwise, creates the section if i t is not present |
72 * yet. Sets the available suggestions on the section. | 75 * yet. Sets the available suggestions on the section. |
73 * @param category The category for which the section must be reset. | 76 * @param category The category for which the section must be reset. |
74 * @param categoryStatus The category status. | 77 * @param categoryStatus The category status. |
75 * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when | 78 * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when |
76 * they are empty, even when they are normally not. | 79 * they are empty, even when they are normally not. |
77 * @return The number of suggestions for the section. | 80 * @return The number of suggestions for the section. |
78 */ | 81 */ |
79 private int resetSection(@CategoryInt int category, @CategoryStatusEnum int categoryStatus, | 82 private int resetSection(@CategoryInt int category, @CategoryStatusEnum int categoryStatus, |
80 boolean alwaysAllowEmptySections) { | 83 boolean alwaysAllowEmptySections) { |
81 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource(); | 84 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource(); |
82 List<SnippetArticle> suggestions = suggestionsSource.getSuggestionsForCa tegory(category); | 85 List<SnippetArticle> suggestions = suggestionsSource.getSuggestionsForCa tegory(category); |
83 SuggestionsCategoryInfo info = suggestionsSource.getCategoryInfo(categor y); | 86 SuggestionsCategoryInfo info = suggestionsSource.getCategoryInfo(categor y); |
84 | 87 |
85 SuggestionsSection section = mSections.get(category); | 88 SuggestionsSection section = mSections.get(category); |
86 | 89 |
87 // Do not show an empty section if not allowed. | 90 // Do not show an empty section if not allowed. |
88 if (suggestions.isEmpty() && !info.showIfEmpty() && !alwaysAllowEmptySec tions) { | 91 if (suggestions.isEmpty() && !info.showIfEmpty() && !alwaysAllowEmptySec tions) { |
89 if (section != null) removeSection(section); | 92 if (section != null) removeSection(section); |
90 return 0; | 93 return 0; |
91 } | 94 } |
92 | 95 |
93 // Create the section if needed. | 96 // Create the section if needed. |
94 if (section == null) { | 97 if (section == null) { |
95 section = new SuggestionsSection(this, mNewTabPageManager, mOfflineP ageBridge, info); | 98 section = new SuggestionsSection( |
99 this, mNewTabPageManager, mSuggestionRanker, mOfflinePageBri dge, info); | |
96 mSections.put(category, section); | 100 mSections.put(category, section); |
101 mSuggestionRanker.registerCategory(category); | |
97 addChild(section); | 102 addChild(section); |
98 } | 103 } |
99 | 104 |
100 // Set the new suggestions. | 105 // Set the new suggestions. |
101 setSuggestions(category, suggestions, categoryStatus, /* replaceExisting = */ true); | 106 setSuggestions(category, suggestions, categoryStatus, /* replaceExisting = */ true); |
102 | 107 |
103 return suggestions.size(); | 108 return suggestions.size(); |
104 } | 109 } |
105 | 110 |
106 @Override | 111 @Override |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
176 * may have no or only partial effect if changing the list of suggestions is not allowed (e.g. | 181 * may have no or only partial effect if changing the list of suggestions is not allowed (e.g. |
177 * because the user has already seen the suggestions). | 182 * because the user has already seen the suggestions). |
178 * @param category The category for which the suggestions should be set. | 183 * @param category The category for which the suggestions should be set. |
179 * @param suggestions The new list of suggestions for the given category. | 184 * @param suggestions The new list of suggestions for the given category. |
180 * @param status The new category status. | 185 * @param status The new category status. |
181 * @param replaceExisting If true, {@code suggestions} replace the current l ist of suggestions. | 186 * @param replaceExisting If true, {@code suggestions} replace the current l ist of suggestions. |
182 * If false, {@code suggestions} are appended to current list of suggestions . | 187 * If false, {@code suggestions} are appended to current list of suggestions . |
183 */ | 188 */ |
184 private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions, | 189 private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions, |
185 @CategoryStatusEnum int status, boolean replaceExisting) { | 190 @CategoryStatusEnum int status, boolean replaceExisting) { |
186 // Count the number of suggestions before this category. | |
187 int globalPositionOffset = 0; | |
188 for (Map.Entry<Integer, SuggestionsSection> entry : mSections.entrySet() ) { | |
189 if (entry.getKey() == category) break; | |
190 globalPositionOffset += entry.getValue().getSuggestionsCount(); | |
191 } | |
192 // Assign global indices to the new suggestions. | |
193 for (SnippetArticle suggestion : suggestions) { | |
194 suggestion.mGlobalPosition = globalPositionOffset + suggestion.mPosi tion; | |
195 } | |
196 | |
197 mSections.get(category).setSuggestions(suggestions, status, replaceExist ing); | 191 mSections.get(category).setSuggestions(suggestions, status, replaceExist ing); |
198 } | 192 } |
199 | 193 |
200 private boolean canLoadSuggestions(@CategoryInt int category, @CategoryStatu sEnum int status) { | 194 private boolean canLoadSuggestions(@CategoryInt int category, @CategoryStatu sEnum int status) { |
201 // We never want to add suggestions from unknown categories. | 195 // We never want to add suggestions from unknown categories. |
202 if (!mSections.containsKey(category)) return false; | 196 if (!mSections.containsKey(category)) return false; |
203 | 197 |
204 // The status may have changed while the suggestions were loading, perha ps they should not | 198 // The status may have changed while the suggestions were loading, perha ps they should not |
205 // be displayed any more. | 199 // be displayed any more. |
206 if (!SnippetsBridge.isCategoryEnabled(status)) { | 200 if (!SnippetsBridge.isCategoryEnabled(status)) { |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
247 /** | 241 /** |
248 * @return Whether the list of sections is empty. | 242 * @return Whether the list of sections is empty. |
249 */ | 243 */ |
250 public boolean isEmpty() { | 244 public boolean isEmpty() { |
251 return mSections.isEmpty(); | 245 return mSections.isEmpty(); |
252 } | 246 } |
253 | 247 |
254 SuggestionsSection getSectionForTesting(@CategoryInt int categoryId) { | 248 SuggestionsSection getSectionForTesting(@CategoryInt int categoryId) { |
255 return mSections.get(categoryId); | 249 return mSections.get(categoryId); |
256 } | 250 } |
251 | |
252 /** | |
253 * Attributes ranks to suggestions and related elements. | |
254 * | |
255 * Ranks here are 0-based scores attributed based on the position or loading order of the | |
256 * elements. See implementation for more details. | |
257 */ | |
258 public static class SuggestionRanker { | |
Bernhard Bauer
2017/01/17 17:42:31
Can we move this to a separate class? There isn't
dgn
2017/01/17 18:46:24
Done.
| |
259 private final Map<Integer, Integer> mSuggestionsAddedPerSection = new Li nkedHashMap<>(); | |
260 private int mTotalAddedSuggestions; | |
261 | |
262 /** | |
263 * Attributes a per section rank to the provided action item. | |
264 * @see ActionItem#getPerSectionRank() | |
265 */ | |
266 public void rankItem(ActionItem actionItem, SuggestionsSection section) { | |
Bernhard Bauer
2017/01/17 17:42:31
Can we rename these methods to rankActionItem() an
dgn
2017/01/17 18:46:24
Done.
| |
267 if (actionItem.getPerSectionRank() != -1) return; // Item was alread y ranked. | |
268 actionItem.setRank(section.getSuggestionsCount()); | |
269 } | |
270 | |
271 /** | |
272 * Attributes global and per section rank to the provided suggestion. | |
273 * @see SnippetArticle#getPerSectionRank() | |
274 * @see SnippetArticle#getGlobalRank() | |
275 */ | |
276 public void rankItem(SnippetArticle suggestion) { | |
277 int globalRank = mTotalAddedSuggestions++; | |
278 int perSectionRank = mSuggestionsAddedPerSection.get(suggestion.mCat egory); | |
279 mSuggestionsAddedPerSection.put(suggestion.mCategory, perSectionRank + 1); | |
280 | |
281 suggestion.setRank(perSectionRank, globalRank); | |
282 } | |
283 | |
284 public void registerCategory(@CategoryInt int category) { | |
285 mSuggestionsAddedPerSection.put(category, 0); | |
286 } | |
287 } | |
257 } | 288 } |
OLD | NEW |