Chromium Code Reviews| 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 |