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 package org.chromium.chrome.browser.ntp.cards; | |
Michael van Ouwerkerk
2016/12/12 16:09:57
Fwiw, I'm not convinced having packages 'cards' an
Bernhard Bauer
2016/12/12 17:39:19
Yeah, I agree, the current split feels kind of arb
Michael van Ouwerkerk
2016/12/13 11:08:21
Yes I considered that split, but we have things li
| |
6 | |
7 import org.chromium.base.Log; | |
8 import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager; | |
9 import org.chromium.chrome.browser.ntp.snippets.CategoryInt; | |
10 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus; | |
11 import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnu m; | |
12 import org.chromium.chrome.browser.ntp.snippets.SnippetArticle; | |
13 import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge; | |
14 import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig; | |
15 import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource; | |
16 import org.chromium.chrome.browser.offlinepages.OfflinePageBridge; | |
17 | |
18 import java.util.ArrayList; | |
19 import java.util.LinkedHashMap; | |
20 import java.util.List; | |
21 import java.util.Map; | |
22 | |
23 /** | |
24 * A node in the tree containing a list of all suggestions sections. It listens to changes in the | |
25 * suggestions source and updates the corresponding sections. | |
26 */ | |
27 public class SectionList extends InnerNode implements SuggestionsSource.Observer { | |
Michael van Ouwerkerk
2016/12/12 16:09:57
It seems it would be nice to see a diff between th
Bernhard Bauer
2016/12/12 17:39:19
Done!
| |
28 private static final String TAG = "Ntp"; | |
29 | |
30 /** Maps suggestion categories to sections, with stable iteration ordering. */ | |
31 private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap <>(); | |
32 private final List<TreeNode> mChildren = new ArrayList<>(); | |
33 private final NewTabPageManager mNewTabPageManager; | |
34 private final OfflinePageBridge mOfflinePageBridge; | |
35 | |
36 public SectionList(NodeParent parent, NewTabPageManager newTabPageManager, | |
37 OfflinePageBridge offlinePageBridge) { | |
38 super(parent); | |
39 mNewTabPageManager = newTabPageManager; | |
40 mNewTabPageManager.getSuggestionsSource().setObserver(this); | |
41 mOfflinePageBridge = offlinePageBridge; | |
42 } | |
43 | |
44 @Override | |
45 public void init() { | |
46 super.init(); | |
47 resetSections(/* alwaysAllowEmptySections = */ false); | |
48 } | |
49 | |
50 @Override | |
51 protected List<TreeNode> getChildren() { | |
52 return mChildren; | |
53 } | |
54 | |
55 /** | |
56 * Resets the sections, reloading the whole new tab page content. | |
57 * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when | |
58 * they are empty, even when they are normally not. | |
59 */ | |
60 public void resetSections(boolean alwaysAllowEmptySections) { | |
61 mSections.clear(); | |
62 mChildren.clear(); | |
63 | |
64 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource(); | |
65 int[] categories = suggestionsSource.getCategories(); | |
66 int[] suggestionsPerCategory = new int[categories.length]; | |
67 int i = 0; | |
68 for (int category : categories) { | |
69 int categoryStatus = suggestionsSource.getCategoryStatus(category); | |
70 if (categoryStatus == CategoryStatus.LOADING_ERROR | |
71 || categoryStatus == CategoryStatus.NOT_PROVIDED | |
72 || categoryStatus == CategoryStatus.CATEGORY_EXPLICITLY_DISA BLED) | |
73 continue; | |
74 | |
75 suggestionsPerCategory[i++] = | |
76 resetSection(category, categoryStatus, alwaysAllowEmptySecti ons); | |
77 } | |
78 | |
79 mNewTabPageManager.trackSnippetsPageImpression(categories, suggestionsPe rCategory); | |
80 } | |
81 | |
82 /** | |
83 * Resets the section for {@code category}. Removes the section if there are no suggestions for | |
84 * it and it is not allowed to be empty. Otherwise, creates the section if i t is not present | |
85 * yet. Sets the available suggestions on the section. | |
86 * @param category The category for which the section must be reset. | |
87 * @param categoryStatus The category status. | |
88 * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when | |
89 * they are empty, even when they are normally not. | |
90 * @return The number of suggestions for the section. | |
91 */ | |
92 private int resetSection(@CategoryInt int category, @CategoryStatusEnum int categoryStatus, | |
93 boolean alwaysAllowEmptySections) { | |
94 SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsS ource(); | |
95 List<SnippetArticle> suggestions = suggestionsSource.getSuggestionsForCa tegory(category); | |
96 SuggestionsCategoryInfo info = suggestionsSource.getCategoryInfo(categor y); | |
97 | |
98 SuggestionsSection section = mSections.get(category); | |
99 | |
100 // Do not show an empty section if not allowed. | |
101 if (suggestions.isEmpty() && !info.showIfEmpty() && !alwaysAllowEmptySec tions) { | |
102 if (section != null) removeSection(section); | |
103 return 0; | |
104 } | |
105 | |
106 // Create the section if needed. | |
107 if (section == null) { | |
108 section = new SuggestionsSection(this, mNewTabPageManager, mOfflineP ageBridge, info); | |
109 mSections.put(category, section); | |
110 mChildren.add(section); | |
111 didAddChild(section); | |
112 } | |
113 | |
114 // Add the new suggestions. | |
115 setSuggestions(category, suggestions, categoryStatus); | |
116 | |
117 return suggestions.size(); | |
118 } | |
119 | |
120 @Override | |
121 public void onNewSuggestions(@CategoryInt int category) { | |
122 @CategoryStatusEnum | |
123 int status = mNewTabPageManager.getSuggestionsSource().getCategoryStatus (category); | |
124 | |
125 if (!canLoadSuggestions(category, status)) return; | |
126 | |
127 // We never want to refresh the suggestions if we already have some cont ent. | |
128 if (mSections.get(category).hasSuggestions()) return; | |
129 | |
130 List<SnippetArticle> suggestions = | |
131 mNewTabPageManager.getSuggestionsSource().getSuggestionsForCateg ory(category); | |
132 | |
133 Log.d(TAG, "Received %d new suggestions for category %d.", suggestions.s ize(), category); | |
134 | |
135 // At first, there might be no suggestions available, we wait until they have been fetched. | |
136 if (suggestions.isEmpty()) return; | |
137 | |
138 setSuggestions(category, suggestions, status); | |
139 } | |
140 | |
141 @Override | |
142 public void onMoreSuggestions(@CategoryInt int category, List<SnippetArticle > suggestions) { | |
143 @CategoryStatusEnum | |
144 int status = mNewTabPageManager.getSuggestionsSource().getCategoryStatus (category); | |
145 if (!canLoadSuggestions(category, status)) return; | |
146 | |
147 setSuggestions(category, suggestions, status); | |
148 } | |
149 | |
150 @Override | |
151 public void onCategoryStatusChanged(@CategoryInt int category, @CategoryStat usEnum int status) { | |
152 // Observers should not be registered for this state. | |
153 assert status != CategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED; | |
154 | |
155 // If there is no section for this category there is nothing to do. | |
156 if (!mSections.containsKey(category)) return; | |
157 | |
158 switch (status) { | |
159 case CategoryStatus.NOT_PROVIDED: | |
160 // The section provider has gone away. Keep open UIs as they are . | |
161 return; | |
162 | |
163 case CategoryStatus.CATEGORY_EXPLICITLY_DISABLED: | |
164 case CategoryStatus.LOADING_ERROR: | |
165 // Need to remove the entire section from the UI immediately. | |
166 removeSection(mSections.get(category)); | |
167 return; | |
168 | |
169 case CategoryStatus.SIGNED_OUT: | |
170 resetSection(category, status, /* alwaysAllowEmptySections = */ false); | |
171 return; | |
172 | |
173 default: | |
174 mSections.get(category).setStatus(status); | |
175 return; | |
176 } | |
177 } | |
178 | |
179 @Override | |
180 public void onSuggestionInvalidated(@CategoryInt int category, String idWith inCategory) { | |
181 if (!mSections.containsKey(category)) return; | |
182 mSections.get(category).removeSuggestionById(idWithinCategory); | |
183 } | |
184 | |
185 @Override | |
186 public void onFullRefreshRequired() { | |
187 resetSections(/* alwaysAllowEmptySections = */false); | |
188 } | |
189 | |
190 private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions, | |
191 @CategoryStatusEnum int status) { | |
192 // Count the number of suggestions before this category. | |
193 int globalPositionOffset = 0; | |
194 for (Map.Entry<Integer, SuggestionsSection> entry : mSections.entrySet() ) { | |
195 if (entry.getKey() == category) break; | |
196 globalPositionOffset += entry.getValue().getSuggestionsCount(); | |
197 } | |
198 // Assign global indices to the new suggestions. | |
199 for (SnippetArticle suggestion : suggestions) { | |
200 suggestion.mGlobalPosition = globalPositionOffset + suggestion.mPosi tion; | |
201 } | |
202 | |
203 mSections.get(category).addSuggestions(suggestions, status); | |
204 } | |
205 | |
206 private boolean canLoadSuggestions(@CategoryInt int category, @CategoryStatu sEnum int status) { | |
207 // We never want to add suggestions from unknown categories. | |
208 if (!mSections.containsKey(category)) return false; | |
209 | |
210 // The status may have changed while the suggestions were loading, perha ps they should not | |
211 // be displayed any more. | |
212 if (!SnippetsBridge.isCategoryEnabled(status)) { | |
213 Log.w(TAG, "Received suggestions for a disabled category (id=%d, sta tus=%d)", category, | |
214 status); | |
215 return false; | |
216 } | |
217 | |
218 return true; | |
219 } | |
220 | |
221 /** | |
222 * Dismisses a section. | |
223 * @param section The section to be dismissed. | |
224 */ | |
225 public void dismissSection(SuggestionsSection section) { | |
226 assert SnippetsConfig.isSectionDismissalEnabled(); | |
227 | |
228 mNewTabPageManager.getSuggestionsSource().dismissCategory(section.getCat egory()); | |
229 removeSection(section); | |
230 } | |
231 | |
232 private void removeSection(SuggestionsSection section) { | |
233 mSections.remove(section.getCategory()); | |
234 willRemoveChild(section); | |
235 mChildren.remove(section); | |
236 } | |
237 | |
238 /** | |
239 * Restores any sections that have been dismissed and triggers a new fetch. | |
240 */ | |
241 public void restoreDismissedSections() { | |
242 mNewTabPageManager.getSuggestionsSource().restoreDismissedCategories(); | |
243 resetSections(/* allowEmptySections = */ true); | |
244 mNewTabPageManager.getSuggestionsSource().fetchRemoteSuggestions(); | |
245 } | |
246 | |
247 /** | |
248 * @return Whether the list of sections is empty. | |
249 */ | |
250 public boolean isEmpty() { | |
251 return mSections.isEmpty(); | |
252 } | |
253 | |
254 SuggestionsSection getSectionForTesting(@CategoryInt int categoryId) { | |
255 return mSections.get(categoryId); | |
256 } | |
257 } | |
OLD | NEW |