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

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

Issue 2513453004: [Android NTP] Move suggestion sections into a separate node. (Closed)
Patch Set: sync 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/NewTabPageAdapter.java
diff --git a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
index aa2fab2040a2760bd113edb1fdf1afa7f78708a6..ebb7b2d15b1f9eaa2fb4843c2fd1e18f55548788 100644
--- a/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
+++ b/chrome/android/java/src/org/chromium/chrome/browser/ntp/cards/NewTabPageAdapter.java
@@ -17,24 +17,16 @@ import android.view.ViewGroup;
import org.chromium.base.Log;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.R;
-import org.chromium.chrome.browser.ntp.NewTabPage.DestructionObserver;
import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
import org.chromium.chrome.browser.ntp.UiConfig;
-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.SectionHeaderViewHolder;
import org.chromium.chrome.browser.ntp.snippets.SnippetArticle;
import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
-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.Arrays;
import java.util.List;
-import java.util.Map;
/**
* A class that handles merging above the fold elements and below the fold cards into an adapter
@@ -42,30 +34,27 @@ import java.util.Map;
* the above-the-fold view (containing the logo, search box, and most visited tiles) and subsequent
* elements will be the cards shown to the user
*/
-public class NewTabPageAdapter
- extends Adapter<NewTabPageViewHolder> implements SuggestionsSource.Observer, NodeParent {
+public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements NodeParent {
private static final String TAG = "Ntp";
private final NewTabPageManager mNewTabPageManager;
private final View mAboveTheFoldView;
private final UiConfig mUiConfig;
private final ItemTouchCallbacks mItemTouchCallbacks = new ItemTouchCallbacks();
- private final OfflinePageBridge mOfflinePageBridge;
private NewTabPageRecyclerView mRecyclerView;
/**
* List of all child nodes (which can themselves contain multiple child nodes).
*/
- private final List<TreeNode> mChildren = new ArrayList<>();
+ private final List<TreeNode> mChildren;
+ private final InnerNode mRoot;
+
private final AboveTheFoldItem mAboveTheFold = new AboveTheFoldItem();
+ private final SectionList mSections;
private final SignInPromo mSigninPromo;
private final AllDismissedItem mAllDismissed;
private final Footer mFooter;
private final SpacingItem mBottomSpacer = new SpacingItem();
- private final InnerNode mRoot;
-
- /** Maps suggestion categories to sections, with stable iteration ordering. */
- private final Map<Integer, SuggestionsSection> mSections = new LinkedHashMap<>();
private class ItemTouchCallbacks extends ItemTouchHelper.Callback {
@Override
@@ -143,104 +132,23 @@ public class NewTabPageAdapter
mNewTabPageManager = manager;
mAboveTheFoldView = aboveTheFoldView;
mUiConfig = uiConfig;
- mOfflinePageBridge = offlinePageBridge;
mRoot = new InnerNode(this) {
@Override
protected List<TreeNode> getChildren() {
return mChildren;
}
-
- @Override
- public void onItemRangeChanged(TreeNode child, int index, int count) {
- if (mChildren.isEmpty()) return; // The sections have not been initialised yet.
- super.onItemRangeChanged(child, index, count);
- }
-
- @Override
- public void onItemRangeInserted(TreeNode child, int index, int count) {
- if (mChildren.isEmpty()) return; // The sections have not been initialised yet.
- super.onItemRangeInserted(child, index, count);
- }
-
- @Override
- public void onItemRangeRemoved(TreeNode child, int index, int count) {
- if (mChildren.isEmpty()) return; // The sections have not been initialised yet.
- super.onItemRangeRemoved(child, index, count);
- }
};
- mSigninPromo = new SignInPromo(mRoot);
+ mSections = new SectionList(mRoot, mNewTabPageManager, offlinePageBridge);
+ mSigninPromo = new SignInPromo(mRoot, mNewTabPageManager);
mAllDismissed = new AllDismissedItem(mRoot);
mFooter = new Footer(mRoot);
- DestructionObserver signInObserver = mSigninPromo.getObserver();
- if (signInObserver != null) mNewTabPageManager.addDestructionObserver(signInObserver);
-
- resetSections(/*alwaysAllowEmptySections=*/false);
- mNewTabPageManager.getSuggestionsSource().setObserver(this);
- }
-
- /**
- * 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);
-
- updateChildren();
- }
-
- /**
- * 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);
-
- // Do not show an empty section if not allowed.
- if (suggestions.isEmpty() && !info.showIfEmpty() && !alwaysAllowEmptySections) {
- mSections.remove(category);
- return 0;
- }
-
- // Create the section if needed.
- SuggestionsSection section = mSections.get(category);
- if (section == null) {
- section = new SuggestionsSection(mRoot, info, mNewTabPageManager, mOfflinePageBridge);
- mSections.put(category, section);
- }
- // Add the new suggestions.
- setSuggestions(category, suggestions, categoryStatus);
+ mChildren = Arrays.asList(
+ mAboveTheFold, mSections, mSigninPromo, mAllDismissed, mFooter, mBottomSpacer);
+ mRoot.init();
- return suggestions.size();
+ updateAllDismissedVisibility();
}
/** Returns callbacks to configure the interactions with the RecyclerView's items. */
@@ -249,74 +157,6 @@ public class NewTabPageAdapter
}
@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:
- // TODO(dgn): We currently can only reach this through an old variation parameter.
- 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);
- }
-
- @Override
@ItemViewType
public int getItemViewType(int position) {
return mRoot.getItemViewType(position);
@@ -355,7 +195,7 @@ public class NewTabPageAdapter
return new Footer.ViewHolder(mRecyclerView, mNewTabPageManager);
case ItemViewType.ALL_DISMISSED:
- return new AllDismissedItem.ViewHolder(mRecyclerView, mNewTabPageManager, this);
+ return new AllDismissedItem.ViewHolder(mRecyclerView, mSections);
}
assert false : viewType;
@@ -387,44 +227,12 @@ public class NewTabPageAdapter
return RecyclerView.NO_POSITION;
}
- int getBottomSpacerPosition() {
- return getChildPositionOffset(mBottomSpacer);
- }
-
int getLastContentItemPosition() {
return getChildPositionOffset(hasAllBeenDismissed() ? mAllDismissed : mFooter);
}
- 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 void updateChildren() {
- mChildren.clear();
- mChildren.add(mAboveTheFold);
- mChildren.addAll(mSections.values());
- mChildren.add(mSigninPromo);
- mChildren.add(mAllDismissed);
- mChildren.add(mFooter);
- mChildren.add(mBottomSpacer);
-
- updateAllDismissedVisibility();
-
- // TODO(mvanouwerkerk): Notify about the subset of changed items. At least |mAboveTheFold|
- // has not changed when refreshing from the all dismissed state.
- notifyDataSetChanged();
+ int getBottomSpacerPosition() {
+ return getChildPositionOffset(mBottomSpacer);
}
private void updateAllDismissedVisibility() {
@@ -433,17 +241,6 @@ public class NewTabPageAdapter
mFooter.setVisible(!showAllDismissed);
}
- private void removeSection(SuggestionsSection section) {
- mSections.remove(section.getCategory());
- int startPos = getChildPositionOffset(section);
- mChildren.remove(section);
- notifyItemRangeRemoved(startPos, section.getItemCount());
-
- updateAllDismissedVisibility();
-
- notifyItemChanged(getBottomSpacerPosition());
- }
-
@Override
public void onItemRangeChanged(TreeNode child, int itemPosition, int itemCount) {
assert child == mRoot;
@@ -495,7 +292,7 @@ public class NewTabPageAdapter
switch (itemViewType) {
case ItemViewType.STATUS:
case ItemViewType.ACTION:
- dismissSection(getSuggestionsSection(position));
+ dismissSection(position);
return;
case ItemViewType.SNIPPET:
@@ -512,13 +309,10 @@ public class NewTabPageAdapter
}
}
- private void dismissSection(SuggestionsSection section) {
- assert SnippetsConfig.isSectionDismissalEnabled();
-
+ private void dismissSection(int position) {
+ SuggestionsSection section = getSuggestionsSection(position);
+ mSections.dismissSection(section);
announceItemRemoved(section.getHeaderText());
-
- mNewTabPageManager.getSuggestionsSource().dismissCategory(section.getCategory());
- removeSection(section);
}
private void dismissSuggestion(int position) {
@@ -560,28 +354,15 @@ public class NewTabPageAdapter
return mSections.isEmpty() && !mSigninPromo.isVisible();
}
- 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;
- }
-
/**
* @param itemPosition The position of an item in the adapter.
* @return Returns the {@link SuggestionsSection} that contains the item at
* {@code itemPosition}, or null if the item is not part of one.
*/
private SuggestionsSection getSuggestionsSection(int itemPosition) {
- TreeNode child = mRoot.getChildForPosition(itemPosition);
+ int relativePosition = itemPosition - mRoot.getStartingOffsetForChild(mSections);
+ assert relativePosition >= 0;
+ TreeNode child = mSections.getChildForPosition(relativePosition);
if (!(child instanceof SuggestionsSection)) return null;
return (SuggestionsSection) child;
}
@@ -604,8 +385,8 @@ public class NewTabPageAdapter
return RecyclerView.NO_POSITION;
}
- SuggestionsSection getSectionForTesting(@CategoryInt int category) {
- return mSections.get(category);
+ SectionList getSectionListForTesting() {
+ return mSections;
}
InnerNode getRootForTesting() {

Powered by Google App Engine
This is Rietveld 408576698