Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(365)

Unified Diff: chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java

Issue 2513453004: [Android NTP] Move suggestion sections into a separate node. (Closed)
Patch Set: fix Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
new file mode 100644
index 0000000000000000000000000000000000000000..2143c590c1b5ea768d29bf718f04b28ba84f0b89
--- /dev/null
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/SectionList.java
@@ -0,0 +1,257 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+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
+
+import org.chromium.base.Log;
+import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
+import org.chromium.chrome.browser.ntp.snippets.CategoryInt;
+import org.chromium.chrome.browser.ntp.snippets.CategoryStatus;
+import org.chromium.chrome.browser.ntp.snippets.CategoryStatus.CategoryStatusEnum;
+import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
+import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
+import org.chromium.chrome.browser.ntp.snippets.SnippetsConfig;
+import org.chromium.chrome.browser.ntp.snippets.SuggestionsSource;
+import org.chromium.chrome.browser.offlinepages.OfflinePageBridge;
+
+import java.util.ArrayList;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A node in the tree containing a list of all suggestions sections. It listens to changes in the
+ * suggestions source and updates the corresponding sections.
+ */
+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!
+ private static final String TAG = "Ntp";
+
+ /** Maps suggestion categories to sections, with stable iteration ordering. */
+ private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap<>();
+ private final List<TreeNode> mChildren = new ArrayList<>();
+ private final NewTabPageManager mNewTabPageManager;
+ private final OfflinePageBridge mOfflinePageBridge;
+
+ public SectionList(NodeParent parent, NewTabPageManager newTabPageManager,
+ OfflinePageBridge offlinePageBridge) {
+ super(parent);
+ mNewTabPageManager = newTabPageManager;
+ mNewTabPageManager.getSuggestionsSource().setObserver(this);
+ mOfflinePageBridge = offlinePageBridge;
+ }
+
+ @Override
+ public void init() {
+ super.init();
+ resetSections(/* alwaysAllowEmptySections = */ false);
+ }
+
+ @Override
+ protected List<TreeNode> getChildren() {
+ return mChildren;
+ }
+
+ /**
+ * Resets the sections, reloading the whole new tab page content.
+ * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when
+ * they are empty, even when they are normally not.
+ */
+ public void resetSections(boolean alwaysAllowEmptySections) {
+ mSections.clear();
+ mChildren.clear();
+
+ SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsSource();
+ int[] categories = suggestionsSource.getCategories();
+ int[] suggestionsPerCategory = new int[categories.length];
+ int i = 0;
+ for (int category : categories) {
+ int categoryStatus = suggestionsSource.getCategoryStatus(category);
+ if (categoryStatus == CategoryStatus.LOADING_ERROR
+ || categoryStatus == CategoryStatus.NOT_PROVIDED
+ || categoryStatus == CategoryStatus.CATEGORY_EXPLICITLY_DISABLED)
+ continue;
+
+ suggestionsPerCategory[i++] =
+ resetSection(category, categoryStatus, alwaysAllowEmptySections);
+ }
+
+ mNewTabPageManager.trackSnippetsPageImpression(categories, suggestionsPerCategory);
+ }
+
+ /**
+ * Resets the section for {@code category}. Removes the section if there are no suggestions for
+ * it and it is not allowed to be empty. Otherwise, creates the section if it is not present
+ * yet. Sets the available suggestions on the section.
+ * @param category The category for which the section must be reset.
+ * @param categoryStatus The category status.
+ * @param alwaysAllowEmptySections Whether sections are always allowed to be displayed when
+ * they are empty, even when they are normally not.
+ * @return The number of suggestions for the section.
+ */
+ private int resetSection(@CategoryInt int category, @CategoryStatusEnum int categoryStatus,
+ boolean alwaysAllowEmptySections) {
+ SuggestionsSource suggestionsSource = mNewTabPageManager.getSuggestionsSource();
+ List<SnippetArticle> suggestions = suggestionsSource.getSuggestionsForCategory(category);
+ SuggestionsCategoryInfo info = suggestionsSource.getCategoryInfo(category);
+
+ SuggestionsSection section = mSections.get(category);
+
+ // Do not show an empty section if not allowed.
+ if (suggestions.isEmpty() && !info.showIfEmpty() && !alwaysAllowEmptySections) {
+ if (section != null) removeSection(section);
+ return 0;
+ }
+
+ // Create the section if needed.
+ if (section == null) {
+ section = new SuggestionsSection(this, mNewTabPageManager, mOfflinePageBridge, info);
+ mSections.put(category, section);
+ mChildren.add(section);
+ didAddChild(section);
+ }
+
+ // Add the new suggestions.
+ setSuggestions(category, suggestions, categoryStatus);
+
+ return suggestions.size();
+ }
+
+ @Override
+ public void onNewSuggestions(@CategoryInt int category) {
+ @CategoryStatusEnum
+ int status = mNewTabPageManager.getSuggestionsSource().getCategoryStatus(category);
+
+ if (!canLoadSuggestions(category, status)) return;
+
+ // We never want to refresh the suggestions if we already have some content.
+ if (mSections.get(category).hasSuggestions()) return;
+
+ List<SnippetArticle> suggestions =
+ mNewTabPageManager.getSuggestionsSource().getSuggestionsForCategory(category);
+
+ Log.d(TAG, "Received %d new suggestions for category %d.", suggestions.size(), category);
+
+ // At first, there might be no suggestions available, we wait until they have been fetched.
+ if (suggestions.isEmpty()) return;
+
+ setSuggestions(category, suggestions, status);
+ }
+
+ @Override
+ public void onMoreSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions) {
+ @CategoryStatusEnum
+ int status = mNewTabPageManager.getSuggestionsSource().getCategoryStatus(category);
+ if (!canLoadSuggestions(category, status)) return;
+
+ setSuggestions(category, suggestions, status);
+ }
+
+ @Override
+ public void onCategoryStatusChanged(@CategoryInt int category, @CategoryStatusEnum int status) {
+ // Observers should not be registered for this state.
+ assert status != CategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED;
+
+ // If there is no section for this category there is nothing to do.
+ if (!mSections.containsKey(category)) return;
+
+ switch (status) {
+ case CategoryStatus.NOT_PROVIDED:
+ // The section provider has gone away. Keep open UIs as they are.
+ return;
+
+ case CategoryStatus.CATEGORY_EXPLICITLY_DISABLED:
+ case CategoryStatus.LOADING_ERROR:
+ // Need to remove the entire section from the UI immediately.
+ removeSection(mSections.get(category));
+ return;
+
+ case CategoryStatus.SIGNED_OUT:
+ resetSection(category, status, /* alwaysAllowEmptySections = */ false);
+ return;
+
+ default:
+ mSections.get(category).setStatus(status);
+ return;
+ }
+ }
+
+ @Override
+ public void onSuggestionInvalidated(@CategoryInt int category, String idWithinCategory) {
+ if (!mSections.containsKey(category)) return;
+ mSections.get(category).removeSuggestionById(idWithinCategory);
+ }
+
+ @Override
+ public void onFullRefreshRequired() {
+ resetSections(/* alwaysAllowEmptySections = */false);
+ }
+
+ private void setSuggestions(@CategoryInt int category, List<SnippetArticle> suggestions,
+ @CategoryStatusEnum int status) {
+ // Count the number of suggestions before this category.
+ int globalPositionOffset = 0;
+ for (Map.Entry<Integer, SuggestionsSection> entry : mSections.entrySet()) {
+ if (entry.getKey() == category) break;
+ globalPositionOffset += entry.getValue().getSuggestionsCount();
+ }
+ // Assign global indices to the new suggestions.
+ for (SnippetArticle suggestion : suggestions) {
+ suggestion.mGlobalPosition = globalPositionOffset + suggestion.mPosition;
+ }
+
+ mSections.get(category).addSuggestions(suggestions, status);
+ }
+
+ private boolean canLoadSuggestions(@CategoryInt int category, @CategoryStatusEnum int status) {
+ // We never want to add suggestions from unknown categories.
+ if (!mSections.containsKey(category)) return false;
+
+ // The status may have changed while the suggestions were loading, perhaps they should not
+ // be displayed any more.
+ if (!SnippetsBridge.isCategoryEnabled(status)) {
+ Log.w(TAG, "Received suggestions for a disabled category (id=%d, status=%d)", category,
+ status);
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * Dismisses a section.
+ * @param section The section to be dismissed.
+ */
+ public void dismissSection(SuggestionsSection section) {
+ assert SnippetsConfig.isSectionDismissalEnabled();
+
+ mNewTabPageManager.getSuggestionsSource().dismissCategory(section.getCategory());
+ removeSection(section);
+ }
+
+ private void removeSection(SuggestionsSection section) {
+ mSections.remove(section.getCategory());
+ willRemoveChild(section);
+ mChildren.remove(section);
+ }
+
+ /**
+ * Restores any sections that have been dismissed and triggers a new fetch.
+ */
+ public void restoreDismissedSections() {
+ mNewTabPageManager.getSuggestionsSource().restoreDismissedCategories();
+ resetSections(/* allowEmptySections = */ true);
+ mNewTabPageManager.getSuggestionsSource().fetchRemoteSuggestions();
+ }
+
+ /**
+ * @return Whether the list of sections is empty.
+ */
+ public boolean isEmpty() {
+ return mSections.isEmpty();
+ }
+
+ SuggestionsSection getSectionForTesting(@CategoryInt int categoryId) {
+ return mSections.get(categoryId);
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698