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

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

Issue 2196273002: Zine: support multiple sections in the ui (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 5 months 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 791ff025f3385521cf87bc6d040a86a0dab29f1f..83941effa5701751644aa31bb0382b0e02c3c0ea 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,6 +17,7 @@ import org.chromium.chrome.browser.ntp.NewTabPageLayout;
import org.chromium.chrome.browser.ntp.NewTabPageUma;
import org.chromium.chrome.browser.ntp.NewTabPageView.NewTabPageManager;
import org.chromium.chrome.browser.ntp.UiConfig;
+import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCategory;
import org.chromium.chrome.browser.ntp.snippets.ContentSuggestionsCategoryStatus;
import org.chromium.chrome.browser.ntp.snippets.SnippetArticleListItem;
import org.chromium.chrome.browser.ntp.snippets.SnippetArticleViewHolder;
@@ -24,9 +25,14 @@ import org.chromium.chrome.browser.ntp.snippets.SnippetHeaderListItem;
import org.chromium.chrome.browser.ntp.snippets.SnippetHeaderViewHolder;
import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge;
import org.chromium.chrome.browser.ntp.snippets.SnippetsBridge.SnippetsObserver;
+import org.chromium.chrome.browser.ntp.snippets.SuggestionsCategory;
+import org.chromium.chrome.browser.ntp.snippets.SuggestionsCategoryStatus;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
/**
* A class that handles merging above the fold elements and below the fold cards into an adapter
@@ -39,17 +45,17 @@ public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements
private final NewTabPageManager mNewTabPageManager;
private final NewTabPageLayout mNewTabPageLayout;
- private final AboveTheFoldListItem mAboveTheFold;
- private final SnippetHeaderListItem mHeader;
+ private final SnippetsBridge mSnippetsBridge;
PEConn 2016/08/01 16:26:51 Just a heads up, when you merge this will be chang
Bernhard Bauer 2016/08/02 13:03:26 Acknowledged.
private final UiConfig mUiConfig;
- private StatusListItem mStatusCard;
- private final SpacingListItem mBottomSpacer;
- private final List<NewTabPageListItem> mItems;
private final ItemTouchCallbacks mItemTouchCallbacks;
private NewTabPageRecyclerView mRecyclerView;
- private int mProviderStatus;
- private SnippetsBridge mSnippetsBridge;
+ private final List<NewTabPageItemGroup> mGroups;
+ private final AboveTheFoldListItem mAboveTheFold;
+ private final SpacingListItem mBottomSpacer;
+
+ // Maps suggestion categories to sections, with stable iteration ordering.
+ private final Map<Integer, SuggestionsSection> mSections;
private class ItemTouchCallbacks extends ItemTouchHelper.Callback {
@Override
@@ -112,17 +118,20 @@ public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements
SnippetsBridge snippetsBridge, UiConfig uiConfig) {
mNewTabPageManager = manager;
mNewTabPageLayout = newTabPageLayout;
- mAboveTheFold = new AboveTheFoldListItem();
- mHeader = new SnippetHeaderListItem();
- mBottomSpacer = new SpacingListItem();
- mItemTouchCallbacks = new ItemTouchCallbacks();
- mItems = new ArrayList<>();
- mProviderStatus = ContentSuggestionsCategoryStatus.INITIALIZING;
mSnippetsBridge = snippetsBridge;
mUiConfig = uiConfig;
- mStatusCard = StatusListItem.create(snippetsBridge.getCategoryStatus(), this);
+ mItemTouchCallbacks = new ItemTouchCallbacks();
- loadSnippets(new ArrayList<SnippetArticleListItem>());
+ mGroups = new ArrayList<>();
PEConn 2016/08/01 16:26:50 Why not initialize these inline where they are dec
Bernhard Bauer 2016/08/02 13:03:26 Done.
+ mAboveTheFold = new AboveTheFoldListItem();
+ mBottomSpacer = new SpacingListItem();
+ mSections = new TreeMap<>();
+
+ // TODO(mvanouwerkerk): Do not hard code ARTICLES. Maybe do not initialize an empty
+ // section in the constructor.
+ setSuggestions(ContentSuggestionsCategory.ARTICLES,
+ Collections.<SnippetArticleListItem>emptyList(),
+ ContentSuggestionsCategoryStatus.INITIALIZING);
mSnippetsBridge.setObserver(this);
}
@@ -132,54 +141,70 @@ public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements
}
@Override
- public void onSnippetsReceived(List<SnippetArticleListItem> snippets) {
+ public void onSuggestionsReceived(
+ @SuggestionsCategory int category, List<SnippetArticleListItem> suggestions) {
// We never want to refresh the suggestions if we already have some content.
- if (hasSuggestions()) return;
+ if (mSections.containsKey(category) && mSections.get(category).hasSuggestions()) return;
- if (!SnippetsBridge.isCategoryStatusInitOrAvailable(mProviderStatus)) {
+ // The status may have changed while the suggestions were loading, perhaps they should not
+ // be displayed any more.
+ if (!SnippetsBridge.isCategoryStatusInitOrAvailable(
+ mSnippetsBridge.getCategoryStatus(category))) {
return;
}
- Log.d(TAG, "Received %d new snippets.", snippets.size());
+ Log.d(TAG, "Received %d new suggestions for category %d.", suggestions.size(), category);
- // At first, there might be no snippets available, we wait until they have been fetched.
- if (snippets.isEmpty()) return;
+ // At first, there might be no suggestions available, we wait until they have been fetched.
+ if (suggestions.isEmpty()) return;
- loadSnippets(snippets);
+ setSuggestions(category, suggestions, mSnippetsBridge.getCategoryStatus(category));
NewTabPageUma.recordSnippetAction(NewTabPageUma.SNIPPETS_ACTION_SHOWN);
}
@Override
- public void onCategoryStatusChanged(int categoryStatus) {
- // Observers should not be registered for that state
- assert categoryStatus
- != ContentSuggestionsCategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED;
-
- mProviderStatus = categoryStatus;
- mStatusCard = StatusListItem.create(mProviderStatus, this);
-
- // We had suggestions but we just got notified about the provider being enabled. Nothing to
- // do then.
- if (SnippetsBridge.isCategoryStatusAvailable(mProviderStatus) && hasSuggestions()) return;
-
- if (hasSuggestions()) {
- // We have suggestions, this implies that the service was previously enabled and just
- // transitioned to a disabled state. Clear them.
- loadSnippets(new ArrayList<SnippetArticleListItem>());
+ public void onCategoryStatusChanged(
+ @SuggestionsCategory int category, @SuggestionsCategoryStatus int status) {
+ // Observers should not be registered for this state.
+ assert status != ContentSuggestionsCategoryStatus.ALL_SUGGESTIONS_EXPLICITLY_DISABLED;
+
+ // If there is no section for this category there is nothing to do.
+ if (!mSections.containsKey(category)) return;
+
+ SuggestionsSection section = mSections.get(category);
+
+ // The section already has suggestions but we just got notified about the provider being
+ // enabled. Nothing to do.
+ if (SnippetsBridge.isCategoryStatusAvailable(status) && section.hasSuggestions()) return;
+
+ if (section.hasSuggestions()) {
+ // The section has suggestions, this implies that the service was previously enabled
+ // and just transitioned to a disabled state. Clear it.
+ setSuggestions(category, Collections.<SnippetArticleListItem>emptyList(), status);
PEConn 2016/08/01 16:26:51 Do we want to pull the rug out from under the user
Marc Treib 2016/08/01 16:34:22 Drive-by: In some cases, yes, namely when the sugg
} else {
- // If there are no suggestions there is an old status card that must be replaced.
- int firstCardPosition = getFirstCardPosition();
- mItems.set(firstCardPosition, mStatusCard);
- // Update both the status card and the spacer after it.
- notifyItemRangeChanged(firstCardPosition, 2);
+ // If the section has no suggestions, it has an old status card that must be replaced.
+ section.setSuggestions(Collections.<SnippetArticleListItem>emptyList(), status, this);
+
+ // Update both the status card, and the bottom spacer, which may get a different height
+ // if the status card height changed.
+ int statusItemPosition = getGroupPositionOffset(section);
+ List<NewTabPageListItem> sectionItems = section.getItems();
+ for (int i = 0; i < sectionItems.size(); i++) {
+ if (sectionItems.get(i) instanceof StatusListItem) {
PEConn 2016/08/01 16:26:51 Won't the StatusListItem only ever be the first it
Bernhard Bauer 2016/08/02 13:03:26 Done.
+ statusItemPosition += i;
+ break;
+ }
+ }
+ notifyItemChanged(statusItemPosition);
+ notifyItemChanged(getBottomSpacerPosition());
}
}
@Override
@NewTabPageListItem.ViewType
public int getItemViewType(int position) {
- return mItems.get(position).getType();
+ return getItems().get(position).getType();
}
@Override
@@ -212,24 +237,28 @@ public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements
@Override
public void onBindViewHolder(NewTabPageViewHolder holder, final int position) {
- holder.onBindViewHolder(mItems.get(position));
+ holder.onBindViewHolder(getItems().get(position));
}
@Override
public int getItemCount() {
- return mItems.size();
+ return getItems().size();
}
public int getAboveTheFoldPosition() {
- return mItems.indexOf(mAboveTheFold);
+ return getItems().indexOf(mAboveTheFold);
}
- public int getHeaderPosition() {
- return mItems.indexOf(mHeader);
+ public int getFirstHeaderPosition() {
+ List<NewTabPageListItem> items = getItems();
+ for (int i = 0; i < items.size(); i++) {
+ if (items.get(i) instanceof SnippetHeaderListItem) return i;
+ }
+ return RecyclerView.NO_POSITION;
}
public int getFirstCardPosition() {
- return getHeaderPosition() + 1;
+ return getFirstHeaderPosition() + 1;
}
public int getLastCardPosition() {
@@ -237,7 +266,7 @@ public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements
}
public int getBottomSpacerPosition() {
- return mItems.indexOf(mBottomSpacer);
+ return getItems().indexOf(mBottomSpacer);
}
/** Start a request for new snippets. */
@@ -245,36 +274,43 @@ public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements
SnippetsBridge.fetchSnippets(/*forceRequest=*/true);
}
- private void loadSnippets(List<SnippetArticleListItem> snippets) {
- // Copy thumbnails over
- for (SnippetArticleListItem snippet : snippets) {
- int existingSnippetIdx = mItems.indexOf(snippet);
- if (existingSnippetIdx == -1) continue;
+ private void setSuggestions(@SuggestionsCategory int category,
+ List<SnippetArticleListItem> suggestions, @SuggestionsCategoryStatus int status) {
+ copyThumbnails(category, suggestions);
- snippet.setThumbnailBitmap(
- ((SnippetArticleListItem) mItems.get(existingSnippetIdx)).getThumbnailBitmap());
- }
+ mGroups.clear();
+ mGroups.add(mAboveTheFold);
- boolean hasContentToShow = !snippets.isEmpty();
-
- // TODO(mvanouwerkerk): Make it so that the header does not need to be manipulated
- // separately from the cards to which it belongs - crbug.com/616090.
- mHeader.setVisible(hasContentToShow);
-
- mItems.clear();
- mItems.add(mAboveTheFold);
- mItems.add(mHeader);
- if (hasContentToShow) {
- mItems.addAll(snippets);
+ if (!mSections.containsKey(category)) {
+ mSections.put(category,
+ new SuggestionsSection(suggestions, status, this));
} else {
- mItems.add(mStatusCard);
+ mSections.get(category).setSuggestions(suggestions, status, this);
}
- mItems.add(mBottomSpacer);
+ for (@SuggestionsCategory Integer key : mSections.keySet()) {
PEConn 2016/08/01 16:26:51 Integer -> int ?
Bernhard Bauer 2016/08/02 13:03:26 Hm, the key type for the Map is a boxed Integer, s
PEConn 2016/08/02 15:45:14 Given we're probably going to have a handful of ca
+ mGroups.add(mSections.get(key));
+ }
+
+ mGroups.add(mBottomSpacer);
notifyDataSetChanged();
}
+ private void copyThumbnails(
+ @SuggestionsCategory int category, List<SnippetArticleListItem> suggestions) {
+ if (!mSections.containsKey(category)) return;
+
+ List<NewTabPageListItem> items = mSections.get(category).getItems();
+ for (SnippetArticleListItem suggestion : suggestions) {
+ int index = items.indexOf(suggestion);
+ if (index == -1) continue;
+
+ suggestion.setThumbnailBitmap(
+ ((SnippetArticleListItem) items.get(index)).getThumbnailBitmap());
+ }
+ }
+
@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
super.onAttachedToRecyclerView(recyclerView);
@@ -293,9 +329,9 @@ public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements
assert itemViewHolder.getItemViewType() == NewTabPageListItem.VIEW_TYPE_SNIPPET;
int position = itemViewHolder.getAdapterPosition();
- SnippetArticleListItem dismissedSnippet = (SnippetArticleListItem) mItems.get(position);
+ SnippetArticleListItem suggestion = (SnippetArticleListItem) getItems().get(position);
- mSnippetsBridge.getSnippedVisited(dismissedSnippet, new Callback<Boolean>() {
+ mSnippetsBridge.getSnippedVisited(suggestion, new Callback<Boolean>() {
@Override
public void onResult(Boolean result) {
NewTabPageUma.recordSnippetAction(result
@@ -304,32 +340,48 @@ public class NewTabPageAdapter extends Adapter<NewTabPageViewHolder> implements
}
});
- mSnippetsBridge.discardSnippet(dismissedSnippet);
- mItems.remove(position);
- notifyItemRemoved(position);
+ mSnippetsBridge.discardSnippet(suggestion);
+ SuggestionsSection section = (SuggestionsSection) getGroup(position);
+ section.dismissSuggestion(suggestion);
- addStatusCardIfNecessary();
+ if (section.hasSuggestions()) {
+ // If one of many suggestions was dismissed, it's a simple item removal, which can be
+ // animated smoothly by the RecyclerView.
+ notifyItemRemoved(position);
+ } else {
+ // If the last suggestion was dismissed, multiple items will have changed, so mark
+ // everything as changed.
+ notifyDataSetChanged();
+ }
}
- private void addStatusCardIfNecessary() {
- if (!hasSuggestions() && !mItems.contains(mStatusCard)) {
- mItems.add(getFirstCardPosition(), mStatusCard);
-
- // We also want to refresh the header and the bottom padding.
- mHeader.setVisible(false);
- notifyDataSetChanged();
+ /**
+ * Returns an unmodifiable list containing all items in the adapter.
+ */
+ List<NewTabPageListItem> getItems() {
Philipp Keck 2016/08/01 15:28:57 Not sure if this is relevant, but this method is c
Bernhard Bauer 2016/08/01 15:37:24 Acknowledged. This is the area where I'm planning
+ List<NewTabPageListItem> items = new ArrayList<>();
+ for (NewTabPageItemGroup group : mGroups) {
+ items.addAll(group.getItems());
}
+ return Collections.unmodifiableList(items);
}
- /** Returns whether we have some suggested content to display. */
- private boolean hasSuggestions() {
- for (NewTabPageListItem item : mItems) {
- if (item instanceof SnippetArticleListItem) return true;
+ private NewTabPageItemGroup getGroup(int itemPosition) {
+ int itemsSkipped = 0;
+ for (NewTabPageItemGroup group : mGroups) {
+ List<NewTabPageListItem> items = group.getItems();
+ itemsSkipped += items.size();
+ if (itemPosition < itemsSkipped) return group;
}
- return false;
+ return null;
}
- List<NewTabPageListItem> getItemsForTesting() {
- return mItems;
+ private int getGroupPositionOffset(NewTabPageItemGroup group) {
+ int positionOffset = 0;
+ for (NewTabPageItemGroup candidateGroup : mGroups) {
+ if (candidateGroup == group) return positionOffset;
+ positionOffset += candidateGroup.getItems().size();
+ }
+ return RecyclerView.NO_POSITION;
}
}

Powered by Google App Engine
This is Rietveld 408576698